mirror of
https://github.com/peeringdb/peeringdb.git
synced 2024-05-11 05:55:09 +00:00
un-accent search on quick search and api filters (#310)
This commit is contained in:
8
Pipfile
8
Pipfile
@@ -1,12 +1,9 @@
|
|||||||
[[source]]
|
[[source]]
|
||||||
|
|
||||||
name = "pypi"
|
name = "pypi"
|
||||||
url = "https://pypi.org/simple"
|
url = "https://pypi.org/simple"
|
||||||
verify_ssl = true
|
verify_ssl = true
|
||||||
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
pytest = ">=2.8.7"
|
pytest = ">=2.8.7"
|
||||||
pytest-cov = ">=2.0.0"
|
pytest-cov = ">=2.0.0"
|
||||||
pytest-django = ">=2.9.1"
|
pytest-django = ">=2.9.1"
|
||||||
@@ -14,9 +11,7 @@ pytest-filedata = ">=0.1.0"
|
|||||||
jsonschema = ">=2.6.0"
|
jsonschema = ">=2.6.0"
|
||||||
facsimile = ">=1.1.1"
|
facsimile = ">=1.1.1"
|
||||||
|
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
|
|
||||||
certifi = "==2017.11.5"
|
certifi = "==2017.11.5"
|
||||||
ipaddress = "==1.0.19"
|
ipaddress = "==1.0.19"
|
||||||
mysqlclient = "==1.3.9"
|
mysqlclient = "==1.3.9"
|
||||||
@@ -56,8 +51,7 @@ django = "==1.11.20"
|
|||||||
uwsgi = "==2.0.14"
|
uwsgi = "==2.0.14"
|
||||||
markdown = "==2.6.7"
|
markdown = "==2.6.7"
|
||||||
"twentyc.rpc" = "==0.3.5"
|
"twentyc.rpc" = "==0.3.5"
|
||||||
|
unidecode = "==1.0.23"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
|
|
||||||
python_version = "2.7"
|
python_version = "2.7"
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/env python
|
#!/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
series of integration/unit tests for the pdb api
|
series of integration/unit tests for the pdb api
|
||||||
"""
|
"""
|
||||||
@@ -2113,6 +2114,34 @@ class TestJSON(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(target, comp)
|
self.assertEqual(target, comp)
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def test_guest_005_list_filter_accented(self):
|
||||||
|
|
||||||
|
"""
|
||||||
|
test filtering with accented search terms
|
||||||
|
"""
|
||||||
|
|
||||||
|
#TODO: sqlite3 is being used as the testing backend, and django 1.11
|
||||||
|
#seems to be unable to set a collation on it, so we can't properly test
|
||||||
|
#the other way atm, for now this test at least confirms that the term is
|
||||||
|
#unaccented correctly.
|
||||||
|
#
|
||||||
|
#on production we run mysql with flattened accents so both ways should work
|
||||||
|
#there regardless.
|
||||||
|
|
||||||
|
org = Organization.objects.create(name="org unaccented", status="ok")
|
||||||
|
net = Network.objects.create(asn=12345, name=u"net unaccented",
|
||||||
|
status="ok", org=org)
|
||||||
|
ix = InternetExchange.objects.create(org=org, name=u"ix unaccented", status="ok")
|
||||||
|
fac = Facility.objects.create(org=org, name=u"fac unaccented", status="ok")
|
||||||
|
|
||||||
|
for tag in ["org","net","ix","fac"]:
|
||||||
|
data = self.db_guest.all(tag, name=u"{} unãccented".format(tag))
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# READONLY PERMISSION TESTS
|
# READONLY PERMISSION TESTS
|
||||||
# These tests assert that the readonly users cannot write anything
|
# These tests assert that the readonly users cannot write anything
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
|
import unidecode
|
||||||
|
|
||||||
from rest_framework import (routers, serializers, status, viewsets)
|
from rest_framework import (routers, serializers, status, viewsets)
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import exception_handler
|
from rest_framework.views import exception_handler
|
||||||
@@ -286,6 +289,8 @@ class ModelViewSet(viewsets.ModelViewSet):
|
|||||||
filters = {}
|
filters = {}
|
||||||
for k, v in self.request.query_params.items():
|
for k, v in self.request.query_params.items():
|
||||||
|
|
||||||
|
v = unidecode.unidecode(v)
|
||||||
|
|
||||||
if k[-3:] == "_id" and k not in field_names:
|
if k[-3:] == "_id" and k not in field_names:
|
||||||
k = k[:-3]
|
k = k[:-3]
|
||||||
|
|
||||||
|
@@ -5,6 +5,10 @@ from peeringdb_server.models import (UTC, InternetExchange, Network, Facility)
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
import unidecode
|
||||||
|
|
||||||
|
def unaccent(v):
|
||||||
|
return unidecode.unidecode(v).lower()
|
||||||
|
|
||||||
# SEARCH INDEX BE STORED HERE
|
# SEARCH INDEX BE STORED HERE
|
||||||
|
|
||||||
@@ -130,7 +134,7 @@ def search(term):
|
|||||||
|
|
||||||
result = {tag: [] for tag, model in ref_dict.items()}
|
result = {tag: [] for tag, model in ref_dict.items()}
|
||||||
|
|
||||||
term = term.lower()
|
term = unaccent(term)
|
||||||
|
|
||||||
# try to convert to int for numeric search matching
|
# try to convert to int for numeric search matching
|
||||||
typed_q = {}
|
typed_q = {}
|
||||||
@@ -153,7 +157,7 @@ def search(term):
|
|||||||
# rid of all the ifs
|
# rid of all the ifs
|
||||||
for tag, index in search_index.items():
|
for tag, index in search_index.items():
|
||||||
for id, data in index.items():
|
for id, data in index.items():
|
||||||
if data.name.lower().find(term) > -1:
|
if unaccent(data.name).find(term) > -1:
|
||||||
result[tag].append({
|
result[tag].append({
|
||||||
"id": id,
|
"id": id,
|
||||||
"name": data.search_result_name,
|
"name": data.search_result_name,
|
||||||
@@ -162,7 +166,7 @@ def search(term):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if hasattr(data,
|
if hasattr(data,
|
||||||
'name_long') and data.name_long.lower().find(term) > -1:
|
'name_long') and unaccent(data.name_long).find(term) > -1:
|
||||||
result[tag].append({
|
result[tag].append({
|
||||||
"id": id,
|
"id": id,
|
||||||
"name": data.search_result_name,
|
"name": data.search_result_name,
|
||||||
@@ -170,7 +174,7 @@ def search(term):
|
|||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if hasattr(data, 'aka') and data.aka.lower().find(term) > -1:
|
if hasattr(data, 'aka') and unaccent(data.aka).find(term) > -1:
|
||||||
result[tag].append({
|
result[tag].append({
|
||||||
"id": id,
|
"id": id,
|
||||||
"name": data.search_result_name,
|
"name": data.search_result_name,
|
||||||
|
@@ -10,6 +10,7 @@ Markdown==2.6.7
|
|||||||
bleach==2.1.3
|
bleach==2.1.3
|
||||||
coreapi==2.3.1
|
coreapi==2.3.1
|
||||||
googlemaps==2.5.1
|
googlemaps==2.5.1
|
||||||
|
Unidecode==1.0.23
|
||||||
|
|
||||||
django-allauth==0.32.0
|
django-allauth==0.32.0
|
||||||
django-autocomplete-light==3.2.9
|
django-autocomplete-light==3.2.9
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Unit-tests for quick search functionality - note that advanced search is not
|
Unit-tests for quick search functionality - note that advanced search is not
|
||||||
tested here as that is using the PDB API entirely.
|
tested here as that is using the PDB API entirely.
|
||||||
@@ -6,6 +7,7 @@ import re
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import unidecode
|
||||||
|
|
||||||
from django.test import TestCase, RequestFactory
|
from django.test import TestCase, RequestFactory
|
||||||
|
|
||||||
@@ -27,20 +29,26 @@ class SearchTests(TestCase):
|
|||||||
search.SEARCH_CACHE["search_index"] = {}
|
search.SEARCH_CACHE["search_index"] = {}
|
||||||
|
|
||||||
cls.instances = {}
|
cls.instances = {}
|
||||||
|
cls.instances_accented = {}
|
||||||
cls.instances_sponsored = {}
|
cls.instances_sponsored = {}
|
||||||
|
|
||||||
# create an instance of each searchable model, so we have something
|
# create an instance of each searchable model, so we have something
|
||||||
# to search for
|
# to search for
|
||||||
cls.org = models.Organization.objects.create(name="Test org")
|
cls.org = models.Organization.objects.create(name="Test org")
|
||||||
for model in search.searchable_models:
|
for model in search.searchable_models:
|
||||||
|
kwargs = {}
|
||||||
if model.handleref.tag == "net":
|
if model.handleref.tag == "net":
|
||||||
kwargs = {"asn": 1}
|
kwargs = {"asn": 1}
|
||||||
else:
|
|
||||||
kwargs = {}
|
|
||||||
cls.instances[model.handleref.tag] = model.objects.create(
|
cls.instances[model.handleref.tag] = model.objects.create(
|
||||||
status="ok", org=cls.org, name="Test %s" % model.handleref.tag,
|
status="ok", org=cls.org, name="Test %s" % model.handleref.tag,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
if model.handleref.tag == "net":
|
||||||
|
kwargs = {"asn": 2}
|
||||||
|
cls.instances_accented[model.handleref.tag] = model.objects.create(
|
||||||
|
status="ok", org=cls.org,
|
||||||
|
name=u"ãccented {}".format(model.handleref.tag), **kwargs)
|
||||||
|
|
||||||
# we also need to test that sponsor ship status comes through
|
# we also need to test that sponsor ship status comes through
|
||||||
# accordingly
|
# accordingly
|
||||||
cls.org_w_sponsorship = models.Organization.objects.create(name="Sponsor org", status="ok")
|
cls.org_w_sponsorship = models.Organization.objects.create(name="Sponsor org", status="ok")
|
||||||
@@ -52,7 +60,7 @@ class SearchTests(TestCase):
|
|||||||
|
|
||||||
for model in search.searchable_models:
|
for model in search.searchable_models:
|
||||||
if model.handleref.tag == "net":
|
if model.handleref.tag == "net":
|
||||||
kwargs = {"asn": 2}
|
kwargs = {"asn": 3}
|
||||||
else:
|
else:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
cls.instances_sponsored[model.handleref.tag] = model.objects.create(
|
cls.instances_sponsored[model.handleref.tag] = model.objects.create(
|
||||||
@@ -140,3 +148,22 @@ class SearchTests(TestCase):
|
|||||||
new_ix_p.delete()
|
new_ix_p.delete()
|
||||||
new_ix_o.delete()
|
new_ix_o.delete()
|
||||||
self.test_search()
|
self.test_search()
|
||||||
|
|
||||||
|
def test_search_unaccent(self):
|
||||||
|
"""
|
||||||
|
search for entities containing 'ãccented' using accented and unaccented
|
||||||
|
terms
|
||||||
|
"""
|
||||||
|
rv = search.search(u"accented")
|
||||||
|
for k, inst in self.instances_accented.items():
|
||||||
|
assert k in rv
|
||||||
|
assert len(rv[k]) == 1
|
||||||
|
assert unidecode.unidecode(rv[k][0]["name"]) == unidecode.unidecode(inst.search_result_name)
|
||||||
|
|
||||||
|
rv = search.search(u"ãccented")
|
||||||
|
for k, inst in self.instances_accented.items():
|
||||||
|
assert k in rv
|
||||||
|
assert len(rv[k]) == 1
|
||||||
|
assert unidecode.unidecode(rv[k][0]["name"]) == unidecode.unidecode(inst.search_result_name)
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user