formdata: add actions_roles index (#8281)

This commit is contained in:
Frédéric Péters 2015-09-16 23:03:11 +02:00
parent 1c20613197
commit ff4b2c152c
3 changed files with 128 additions and 11 deletions

View File

@ -774,6 +774,31 @@ def test_migration_2_formdef_id_in_views():
conn.commit()
cur.close()
@postgresql
def test_migration_6_actions_roles():
conn, cur = sql.get_connection_and_cursor()
cur.execute('UPDATE wcs_meta SET value = 5 WHERE key = %s', ('sql_level',))
cur.execute('DROP VIEW wcs_all_forms')
# hack a formdef table the wrong way, to check it is reconstructed
# properly before the views are created
formdef.fields[4] = fields.StringField(id='4', label='item')
cur.execute('DROP VIEW wcs_view_1_tests')
cur.execute('ALTER TABLE formdata_1_tests DROP COLUMN actions_roles_array')
sql.drop_views(formdef, conn, cur)
formdef.fields[4] = fields.ItemField(id='4', label='item', items=('apple', 'pear', 'peach', 'apricot')),
assert not column_exists_in_table(cur, 'formdata_1_tests', 'actions_roles_array')
sql.migrate()
assert column_exists_in_table(cur, 'formdata_1_tests', 'actions_roles_array')
assert column_exists_in_table(cur, 'wcs_view_1_tests', 'actions_roles_array')
assert column_exists_in_table(cur, 'wcs_all_forms', 'actions_roles_array')
conn.commit()
cur.close()
def drop_formdef_tables():
conn, cur = sql.get_connection_and_cursor()
cur.execute('''SELECT table_name FROM information_schema.tables''')
@ -917,3 +942,62 @@ def test_select_any_formdata():
objects2 = sql.AnyFormData.select(order_by='receipt_time', limit=10, offset=20)
assert [(x.formdef_id, x.id) for x in objects2] == [(x.formdef_id, x.id) for x in objects][20:30]
@postgresql
def test_actions_roles():
drop_formdef_tables()
conn, cur = sql.get_connection_and_cursor()
wf = Workflow(name='test endpoint')
st1 = wf.add_status('Status1', 'st1')
st2 = wf.add_status('Status2', 'st2')
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = ['_submitter', '1']
st1.items.append(commentable)
commentable.parent = st1
wf.store()
assert [x.id for x in wf.get_endpoint_status()] == ['st2']
formdef = FormDef()
formdef.name = 'test actions roles'
formdef.fields = []
formdef.workflow = wf
formdef.store()
data_class = formdef.data_class(mode='sql')
formdata = data_class()
formdata.status = 'wf-st1'
formdata.store()
formdata_id = formdata.id
formdata = data_class()
formdata.status = 'wf-st2'
formdata.store()
cur.execute('''SELECT COUNT(*) FROM wcs_all_forms''')
assert bool(cur.fetchone()[0] == 2)
cur.execute('''SELECT COUNT(*) FROM wcs_all_forms
WHERE actions_roles_array && ARRAY['5', '1', '4']''')
assert bool(cur.fetchone()[0] == 1)
# check a change to workflow is reflected in the database
st1.items[0].by = ['2', '3']
wf.store()
cur.execute('''SELECT COUNT(*) FROM wcs_all_forms
WHERE actions_roles_array && ARRAY['5', '1', '4']''')
assert bool(cur.fetchone()[0] == 0)
cur.execute('''SELECT COUNT(*) FROM wcs_all_forms
WHERE actions_roles_array && ARRAY['2', '3']''')
assert bool(cur.fetchone()[0] == 1)
# using criterias
criterias = [st.Intersects('actions_roles_array', ['2', '3'])]
total_count = sql.AnyFormData.count(criterias)
formdatas = sql.AnyFormData.select(criterias)
assert total_count == 1
assert formdatas[0].id == formdata_id

View File

@ -143,7 +143,7 @@ class Evolution(object):
class FormData(StorableObject):
_names = 'XX'
_hashed_indexes = ['user_id', 'user_hash', 'status', 'workflow_roles',
'concerned_roles']
'concerned_roles', 'actions_roles']
id_display = None
@ -462,7 +462,7 @@ class FormData(StorableObject):
get_substitution_variables_list = classmethod(get_substitution_variables_list)
def rebuild_security(cls):
cls.rebuild_indexes(indexes=['concerned_roles'])
cls.rebuild_indexes(indexes=['concerned_roles', 'actions_roles'])
rebuild_security = classmethod(rebuild_security)
def is_submitter(self, user):
@ -499,10 +499,22 @@ class FormData(StorableObject):
wf_status = self.get_status()
if not wf_status:
status_action_roles.add('_submitter')
return status_action_roles
else:
status_action_roles |= set(self.get_actions_roles())
return status_action_roles
# people that can act on a workflow status item are considered
# 'concerned' by the formdata
concerned_roles = property(get_concerned_roles)
def get_actions_roles(self):
if self.is_draft():
return []
wf_status = self.get_status()
if not wf_status:
return []
from wcs.workflows import get_role_translation
status_action_roles = set()
for item in wf_status.items or []:
if not hasattr(item, 'by') or not item.by:
continue
@ -510,11 +522,13 @@ class FormData(StorableObject):
if role == '_submitter':
status_action_roles.add(role)
else:
status_action_roles.add(get_role_translation(self, role))
real_role = get_role_translation(self, role)
if real_role:
status_action_roles.add(real_role)
return status_action_roles
concerned_roles = property(get_concerned_roles)
actions_roles = property(get_actions_roles)
def get_last_update_time(self):
if self.evolution and self.evolution[-1].time:

View File

@ -306,7 +306,8 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
needed_fields = set(['id', 'user_id', 'user_hash', 'receipt_time',
'status', 'workflow_data', 'id_display', 'fts', 'page_no',
'anonymised', 'workflow_roles', 'workflow_roles_array',
'concerned_roles_array', 'tracking_code'])
'concerned_roles_array', 'tracking_code',
'actions_roles_array'])
# migrations
if not 'fts' in existing_fields:
@ -320,6 +321,8 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
cur.execute('''ALTER TABLE %s ADD COLUMN workflow_roles_array text[]''' % table_name)
if not 'concerned_roles_array' in existing_fields:
cur.execute('''ALTER TABLE %s ADD COLUMN concerned_roles_array text[]''' % table_name)
if not 'actions_roles_array' in existing_fields:
cur.execute('''ALTER TABLE %s ADD COLUMN actions_roles_array text[]''' % table_name)
if not 'page_no' in existing_fields:
cur.execute('''ALTER TABLE %s ADD COLUMN page_no varchar''' % table_name)
@ -369,6 +372,8 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
actions = []
if not 'concerned_roles_array' in existing_fields:
actions.append('rebuild_security')
elif not 'actions_roles_array' in existing_fields:
actions.append('rebuild_security')
if not 'tracking_code' in existing_fields:
# if tracking code has just been added to the table we need to make
# sure the tracking code table does exist.
@ -577,6 +582,7 @@ def do_views(formdef, conn, cur, rebuild_global_views=True):
'''is_at_endpoint'''))
view_fields.append(('concerned_roles_array', 'concerned_roles_array'))
view_fields.append(('actions_roles_array', 'actions_roles_array'))
view_fields.append(('fts', 'fts'))
fields_list = ', '.join(['%s AS %s' % x for x in view_fields])
@ -621,6 +627,7 @@ def do_global_views(conn, cur):
fake_formdef = FormDef()
common_fields = get_view_fields(fake_formdef)
common_fields.append(('concerned_roles_array', 'concerned_roles_array'))
common_fields.append(('actions_roles_array', 'actions_roles_array'))
common_fields.append(('fts', 'fts'))
common_fields.append(('is_at_endpoint', 'is_at_endpoint'))
@ -913,6 +920,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
('workflow_roles', 'bytea'),
('workflow_roles_array', 'text[]'),
('concerned_roles_array', 'text[]'),
('actions_roles_array', 'text[]'),
('tracking_code', 'varchar'),
]
@ -1018,6 +1026,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
sql_dict['workflow_roles_array'] = None
sql_dict['concerned_roles_array'] = [str(x) for x in self.concerned_roles if x]
sql_dict['actions_roles_array'] = [str(x) for x in self.actions_roles if x]
sql_dict.update(self.get_sql_dict_from_data(self.data, self._formdef))
conn, cur = get_connection_and_cursor()
@ -1229,10 +1238,14 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
formdatas = cls.select()
conn, cur = get_connection_and_cursor()
for formdata in formdatas:
sql_statement = '''UPDATE %s SET concerned_roles_array = %%(roles)s WHERE id = %%(id)s''' % cls._table_name
sql_statement = '''UPDATE %s
SET concerned_roles_array = %%(roles)s,
actions_roles_array = %%(actions_roles)s
WHERE id = %%(id)s''' % cls._table_name
cur.execute(sql_statement, {
'id': formdata.id,
'roles': [str(x) for x in formdata.concerned_roles if x]})
'roles': [str(x) for x in formdata.concerned_roles if x],
'actions_roles': [str(x) for x in formdata.actions_roles if x]})
conn.commit()
cur.close()
rebuild_security = classmethod(rebuild_security)
@ -1640,7 +1653,7 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None):
return result
SQL_LEVEL = 5
SQL_LEVEL = 6
def migrate_global_views(conn, cur):
cur.execute('''SELECT COUNT(*) FROM information_schema.tables
@ -1683,6 +1696,12 @@ def migrate():
if sql_level < 5:
# 5: add concerned_roles_array, is_at_endpoint and fts to views
migrate_views(conn, cur)
if sql_level < 6:
# 6: add actions_roles_array to tables and views
from wcs.formdef import FormDef
migrate_views(conn, cur)
for formdef in FormDef.select():
formdef.data_class().rebuild_security()
cur.execute('''UPDATE wcs_meta SET value = %s WHERE key = %s''', (
str(SQL_LEVEL), 'sql_level'))