general: search for users without looking at accents (#13061)
This commit is contained in:
parent
0dfa04e91f
commit
1f1ebe035f
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
import json
|
||||
import shutil
|
||||
|
@ -1502,6 +1504,21 @@ def test_users(pub, local_user):
|
|||
resp = get_app(pub).get(sign_uri('/api/users/?q=foobar'))
|
||||
assert len(resp.json['data']) == 0
|
||||
|
||||
def test_users_unaccent(pub, local_user):
|
||||
local_user.name = 'Jean Sénisme'
|
||||
local_user.store()
|
||||
resp = get_app(pub).get(sign_uri('/api/users/?q=jean'))
|
||||
assert resp.json['data'][0]['user_email'] == local_user.email
|
||||
|
||||
resp = get_app(pub).get(sign_uri('/api/users/?q=senisme'))
|
||||
assert resp.json['data'][0]['user_email'] == local_user.email
|
||||
|
||||
resp = get_app(pub).get(sign_uri('/api/users/?q=sénisme'))
|
||||
assert resp.json['data'][0]['user_email'] == local_user.email
|
||||
|
||||
resp = get_app(pub).get(sign_uri('/api/users/?q=blah'))
|
||||
assert len(resp.json['data']) == 0
|
||||
|
||||
def test_workflow_trigger(pub, local_user):
|
||||
workflow = Workflow(name='test')
|
||||
st1 = workflow.add_status('Status1', 'st1')
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import random
|
||||
|
@ -1040,6 +1042,31 @@ def test_migration_12_users_fts():
|
|||
conn.commit()
|
||||
cur.close()
|
||||
|
||||
@postgresql
|
||||
def test_migration_21_users_ascii_name():
|
||||
conn, cur = sql.get_connection_and_cursor()
|
||||
cur.execute('UPDATE wcs_meta SET value = 11 WHERE key = %s', ('sql_level',))
|
||||
|
||||
sql.SqlUser.wipe()
|
||||
|
||||
user = sql.SqlUser()
|
||||
user.name = 'Jean Sénisme'
|
||||
user.store()
|
||||
|
||||
# remove the ascii_name column
|
||||
cur.execute('ALTER TABLE users DROP COLUMN ascii_name')
|
||||
assert not column_exists_in_table(cur, 'users', 'ascii_name')
|
||||
sql.migrate()
|
||||
|
||||
assert column_exists_in_table(cur, 'users', 'ascii_name')
|
||||
assert migration_level(cur) >= 21
|
||||
|
||||
# make sure the ascii_name is filled after the migration
|
||||
assert sql.SqlUser.count([st.Equal('ascii_name', 'jean senisme')]) == 1
|
||||
|
||||
conn.commit()
|
||||
cur.close()
|
||||
|
||||
def drop_formdef_tables():
|
||||
conn, cur = sql.get_connection_and_cursor()
|
||||
cur.execute('''SELECT table_name FROM information_schema.tables''')
|
||||
|
|
|
@ -528,7 +528,10 @@ class ApiUsersDirectory(Directory):
|
|||
if query:
|
||||
from admin.settings import UserFieldsFormDef
|
||||
formdef = UserFieldsFormDef()
|
||||
criteria_fields = [st.ILike('name', query), st.ILike('email', query)]
|
||||
criteria_fields = [
|
||||
st.ILike('name', query),
|
||||
st.ILike('ascii_name', misc.simplify(query, ' ')),
|
||||
st.ILike('email', query)]
|
||||
for field in formdef.fields:
|
||||
if field.type in ('string', 'text', 'email'):
|
||||
criteria_fields.append(st.ILike('f%s' % field.id, query))
|
||||
|
|
|
@ -343,7 +343,10 @@ class UsersViewDirectory(Directory):
|
|||
return r.getvalue()
|
||||
|
||||
formdef = UserFieldsFormDef()
|
||||
criteria_fields = [ILike('name', query), ILike('email', query)]
|
||||
criteria_fields = [
|
||||
ILike('name', query),
|
||||
ILike('ascii_name', misc.simplify(query, ' ')),
|
||||
ILike('email', query)]
|
||||
for field in formdef.get_all_fields():
|
||||
if field.type in ('string', 'text', 'email'):
|
||||
criteria_fields.append(ILike('f%s' % field.id, query))
|
||||
|
|
25
wcs/sql.py
25
wcs/sql.py
|
@ -473,6 +473,7 @@ def do_user_table():
|
|||
if cur.fetchone()[0] == 0:
|
||||
cur.execute('''CREATE TABLE %s (id serial PRIMARY KEY,
|
||||
name varchar,
|
||||
ascii_name varchar,
|
||||
email varchar,
|
||||
roles text[],
|
||||
is_admin bool,
|
||||
|
@ -487,7 +488,7 @@ def do_user_table():
|
|||
|
||||
needed_fields = set(['id', 'name', 'email', 'roles', 'is_admin',
|
||||
'anonymous', 'name_identifiers', 'verified_fields',
|
||||
'lasso_dump', 'last_seen', 'fts'])
|
||||
'lasso_dump', 'last_seen', 'fts', 'ascii_name'])
|
||||
|
||||
from admin.settings import UserFieldsFormDef
|
||||
formdef = UserFieldsFormDef()
|
||||
|
@ -523,6 +524,9 @@ def do_user_table():
|
|||
if not 'verified_fields' in existing_fields:
|
||||
cur.execute('ALTER TABLE %s ADD COLUMN verified_fields text[]' % table_name)
|
||||
|
||||
if not 'ascii_name' in existing_fields:
|
||||
cur.execute('ALTER TABLE %s ADD COLUMN ascii_name varchar' % table_name)
|
||||
|
||||
# delete obsolete fields
|
||||
for field in (existing_fields - needed_fields):
|
||||
cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field))
|
||||
|
@ -1287,6 +1291,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
|
|||
user = self.get_user()
|
||||
if user:
|
||||
fts_strings.append(user.get_display_name())
|
||||
fts_strings.append(user.ascii_name)
|
||||
|
||||
sql_statement = '''UPDATE %s SET fts = to_tsvector( %%(fts)s)
|
||||
WHERE id = %%(id)s''' % self._table_name
|
||||
|
@ -1448,7 +1453,8 @@ class SqlUser(SqlMixin, wcs.users.User):
|
|||
('name_identifiers', 'varchar[]'),
|
||||
('verified_fields', 'varchar[]'),
|
||||
('lasso_dump', 'text'),
|
||||
('last_seen', 'timestamp')
|
||||
('last_seen', 'timestamp'),
|
||||
('ascii_name', 'varchar'),
|
||||
]
|
||||
|
||||
id = None
|
||||
|
@ -1463,6 +1469,7 @@ class SqlUser(SqlMixin, wcs.users.User):
|
|||
def store(self):
|
||||
sql_dict = {
|
||||
'name': self.name,
|
||||
'ascii_name': self.ascii_name,
|
||||
'email': self.email,
|
||||
'roles': self.roles,
|
||||
'is_admin': self.is_admin,
|
||||
|
@ -1507,6 +1514,7 @@ class SqlUser(SqlMixin, wcs.users.User):
|
|||
fts_strings = []
|
||||
if self.name:
|
||||
fts_strings.append(self.name)
|
||||
fts_strings.append(self.ascii_name)
|
||||
if self.email:
|
||||
fts_strings.append(self.email)
|
||||
if user_formdef and user_formdef.fields:
|
||||
|
@ -1535,7 +1543,7 @@ class SqlUser(SqlMixin, wcs.users.User):
|
|||
o = cls()
|
||||
(o.id, o.name, o.email, o.roles, o.is_admin, o.anonymous,
|
||||
o.name_identifiers, o.verified_fields, o.lasso_dump,
|
||||
o.last_seen) = tuple(row[:10])
|
||||
o.last_seen, ascii_name) = tuple(row[:11])
|
||||
if o.last_seen:
|
||||
o.last_seen = time.mktime(o.last_seen.timetuple())
|
||||
if o.roles:
|
||||
|
@ -1870,7 +1878,7 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None):
|
|||
return result
|
||||
|
||||
|
||||
SQL_LEVEL = 20
|
||||
SQL_LEVEL = 21
|
||||
|
||||
def migrate_global_views(conn, cur):
|
||||
cur.execute('''SELECT COUNT(*) FROM information_schema.tables
|
||||
|
@ -1918,11 +1926,12 @@ def migrate():
|
|||
# 19: add geolocation to views
|
||||
# 20: remove user hash stuff
|
||||
migrate_views(conn, cur)
|
||||
if sql_level < 16:
|
||||
if sql_level < 21:
|
||||
# 3: introduction of _structured for user fields
|
||||
# 4: removal of identification_token
|
||||
# 12: (first part) add fts to users
|
||||
# 16: add verified_fields to users
|
||||
# 21: (first part) add ascii_name to users
|
||||
do_user_table()
|
||||
if sql_level < 6:
|
||||
# 6: add actions_roles_array to tables and views
|
||||
|
@ -1930,13 +1939,15 @@ def migrate():
|
|||
migrate_views(conn, cur)
|
||||
for formdef in FormDef.select():
|
||||
formdef.data_class().rebuild_security()
|
||||
if sql_level < 12:
|
||||
if sql_level < 21:
|
||||
# 12: (second part), store fts in existing rows
|
||||
# 21: (second part), store ascii_name of users
|
||||
for user_id in SqlUser.keys():
|
||||
SqlUser.get(user_id).store()
|
||||
if sql_level < 18:
|
||||
if sql_level < 21:
|
||||
# 17: store last_update_time in tables
|
||||
# 18: add user name to full-text search index
|
||||
# 21: (third part), add user ascii_names to full-text index
|
||||
# load and store all formdatas
|
||||
from wcs.formdef import FormDef
|
||||
for formdef in FormDef.select():
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from qommon import _
|
||||
from qommon.misc import simplify
|
||||
from qommon.storage import StorableObject
|
||||
from qommon import get_cfg
|
||||
import wcs.qommon.storage as st
|
||||
|
@ -70,6 +71,10 @@ class User(StorableObject):
|
|||
from admin.settings import UserFieldsFormDef
|
||||
return UserFieldsFormDef()
|
||||
|
||||
@property
|
||||
def ascii_name(self):
|
||||
return simplify(self.get_display_name(), space=' ')
|
||||
|
||||
def get_display_name(self):
|
||||
if self.name:
|
||||
return self.name
|
||||
|
|
Loading…
Reference in New Issue