0001-misc-remap-statuses-in-a-transaction-38579.patch
tests/admin_pages/test_form.py | ||
---|---|---|
602 | 602 |
formdata2.status = 'draft' |
603 | 603 |
formdata2.store() |
604 | 604 | |
605 |
formdata3 = data_class() |
|
606 |
formdata3.status = 'wf-1' |
|
607 |
formdata3.store() |
|
608 | ||
605 | 609 |
Workflow.wipe() |
606 | 610 |
workflow = Workflow(name='Workflow One') |
607 | 611 |
workflow.store() |
... | ... | |
627 | 631 |
assert len(resp.forms[0]['mapping-%s' % status.id].options) == 1 |
628 | 632 |
assert data_class.get(formdata1.id).status == 'wf-new' |
629 | 633 |
assert data_class.get(formdata2.id).status == 'draft' |
634 |
assert data_class.get(formdata3.id).status == 'wf-1' |
|
630 | 635 |
resp = resp.forms[0].submit() |
631 | 636 | |
632 | 637 |
# run a SQL SELECT and we known all columns are defined. |
... | ... | |
634 | 639 | |
635 | 640 |
assert data_class.get(formdata1.id).status == 'wf-finished' |
636 | 641 |
assert data_class.get(formdata2.id).status == 'draft' |
642 |
assert data_class.get(formdata3.id).status == 'wf-1-invalid-default' |
|
637 | 643 | |
638 | 644 |
# change to another workflow, with no mapping change |
639 | 645 |
workflow2 = workflow |
... | ... | |
658 | 664 |
resp = resp.forms[0].submit() |
659 | 665 |
assert data_class.get(formdata1.id).status == 'wf-finished' |
660 | 666 |
assert data_class.get(formdata2.id).status == 'draft' |
667 |
assert data_class.get(formdata3.id).status == 'wf-1-invalid-default' |
|
661 | 668 | |
662 | 669 |
# run a SQL SELECT and we known all columns are defined. |
663 | 670 |
FormDef.get(formdef.id).data_class().select() |
wcs/admin/forms.py | ||
---|---|---|
1100 | 1100 |
r += form.render() |
1101 | 1101 |
return r.getvalue() |
1102 | 1102 |
else: |
1103 |
workflow_id = form.get_widget('workflow_id').parse() |
|
1104 |
if self.formdef.data_class().keys():
|
|
1103 |
workflow_id = form.get_widget('workflow_id').parse() or self.formdef_default_workflow
|
|
1104 |
if self.formdef.data_class().count():
|
|
1105 | 1105 |
# there are existing formdata, status will have to be mapped |
1106 |
if workflow_id is None: |
|
1107 |
workflow_id = self.formdef_default_workflow |
|
1108 | 1106 |
return redirect('workflow-status-remapping?new=%s' % workflow_id) |
1109 |
self.formdef.workflow = Workflow.get(workflow_id) if workflow_id else None |
|
1110 |
self.formdef.store(comment=_('Workflow change')) |
|
1107 |
self.formdef.change_workflow(Workflow.get(workflow_id)) |
|
1111 | 1108 |
return redirect('.') |
1112 | 1109 | |
1113 | 1110 |
def workflow_status_remapping(self): |
... | ... | |
1146 | 1143 |
r += form.render() |
1147 | 1144 |
return r.getvalue() |
1148 | 1145 |
else: |
1149 |
get_logger().info( |
|
1150 |
'admin - form "%s", workflow is now "%s" (was "%s")' |
|
1151 |
% (self.formdef.name, new_workflow.name, self.formdef.workflow.name) |
|
1152 |
) |
|
1153 |
self.workflow_status_remapping_submit(form) |
|
1154 |
if new_workflow.id == self.formdef_default_workflow: |
|
1155 |
self.formdef.workflow = None |
|
1156 |
else: |
|
1157 |
self.formdef.workflow = Workflow.get(new_workflow.id) |
|
1158 |
self.formdef.store(comment=_('Workflow change')) |
|
1159 |
# instruct formdef to update its security rules |
|
1160 |
self.formdef.data_class().rebuild_security() |
|
1161 |
return redirect('.') |
|
1146 |
return self.workflow_status_remapping_submit(form, new_workflow) |
|
1162 | 1147 | |
1163 |
def workflow_status_remapping_submit(self, form): |
|
1148 |
def workflow_status_remapping_submit(self, form, new_workflow): |
|
1149 |
get_logger().info( |
|
1150 |
'admin - form "%s", workflow is now "%s" (was "%s")' |
|
1151 |
% (self.formdef.name, new_workflow.name, self.formdef.workflow.name) |
|
1152 |
) |
|
1164 | 1153 |
status_mapping = {} |
1165 | 1154 |
for status in self.formdef.workflow.possible_status: |
1166 |
status_mapping['wf-%s' % status.id] = 'wf-%s' % form.get_widget('mapping-%s' % status.id).parse() |
|
1167 |
if any(x[0] != x[1] for x in status_mapping.items()): |
|
1168 |
# if there are status changes, update all formdatas (except drafts) |
|
1169 |
status_mapping.update({'draft': 'draft'}) |
|
1170 |
for item in self.formdef.data_class().select([NotEqual('status', 'draft')]): |
|
1171 |
item.status = status_mapping.get(item.status) |
|
1172 |
if item.evolution: |
|
1173 |
for evo in item.evolution: |
|
1174 |
evo.status = status_mapping.get(evo.status) |
|
1175 |
item.store() |
|
1155 |
status_mapping[status.id] = form.get_widget('mapping-%s' % status.id).parse() |
|
1156 |
self.formdef.change_workflow(new_workflow, status_mapping) |
|
1157 |
return redirect('.') |
|
1176 | 1158 | |
1177 | 1159 |
def get_preview(self): |
1178 | 1160 |
form = Form(action='#', use_tokens=False) |
wcs/formdef.py | ||
---|---|---|
41 | 41 |
from .qommon.form import Form, HtmlWidget, UploadedFile |
42 | 42 |
from .qommon.misc import JSONEncoder, get_as_datetime, simplify, xml_node_text |
43 | 43 |
from .qommon.publisher import get_publisher_class |
44 |
from .qommon.storage import Equal, StorableObject, fix_key |
|
44 |
from .qommon.storage import Equal, NotEqual, StorableObject, fix_key
|
|
45 | 45 |
from .qommon.substitution import Substitutions |
46 | 46 |
from .qommon.template import Template |
47 | 47 |
from .roles import logged_users_role |
... | ... | |
505 | 505 |
return workflow |
506 | 506 | |
507 | 507 |
def set_workflow(self, workflow): |
508 |
if workflow: |
|
508 |
if workflow and workflow.id not in ['_carddef_default', '_default']:
|
|
509 | 509 |
self.workflow_id = workflow.id |
510 | 510 |
self._workflow = workflow |
511 | 511 |
elif self.workflow_id: |
512 | 512 |
self.workflow_id = None |
513 |
self._workflow = None |
|
513 | 514 | |
514 | 515 |
workflow = property(get_workflow, set_workflow) |
515 | 516 | |
... | ... | |
1666 | 1667 |
# chunk contains the fields. |
1667 | 1668 |
return pickle.dumps(object, protocol=2) + pickle.dumps(object.fields, protocol=2) |
1668 | 1669 | |
1670 |
def change_workflow(self, new_workflow, status_mapping=None): |
|
1671 |
old_workflow = self.get_workflow() |
|
1672 | ||
1673 |
formdata_count = self.data_class().count() |
|
1674 |
if formdata_count: |
|
1675 |
assert status_mapping, 'status mapping is required if there are formdatas' |
|
1676 |
assert all( |
|
1677 |
status.id in status_mapping for status in old_workflow.possible_status |
|
1678 |
), 'a status was not mapped' |
|
1679 | ||
1680 |
unmapped_status_suffix = '-invalid-%s' % str(self.workflow_id or 'default') |
|
1681 |
mapping = {} |
|
1682 |
for old_status, new_status in status_mapping.items(): |
|
1683 |
mapping['wf-%s' % old_status] = 'wf-%s' % new_status |
|
1684 |
mapping['draft'] = 'draft' |
|
1685 | ||
1686 |
if any(x[0] != x[1] for x in mapping.items()): |
|
1687 |
# if there are status changes, update all formdatas (except drafts) |
|
1688 |
if get_publisher().is_using_postgresql(): |
|
1689 |
from . import sql |
|
1690 | ||
1691 |
sql.formdef_remap_statuses(self, mapping) |
|
1692 |
else: |
|
1693 | ||
1694 |
def map_status(status): |
|
1695 |
if status is None: |
|
1696 |
return None |
|
1697 |
elif status in mapping: |
|
1698 |
return mapping[status] |
|
1699 |
elif '-invalid-' in status: |
|
1700 |
return status |
|
1701 |
else: |
|
1702 |
return '%s%s' % (status, unmapped_status_suffix) |
|
1703 | ||
1704 |
for formdata in self.data_class().select([NotEqual('status', 'draft')]): |
|
1705 |
formdata.status = map_status(formdata.status) |
|
1706 |
if formdata.evolution: |
|
1707 |
for evo in formdata.evolution: |
|
1708 |
evo.status = map_status(evo.status) |
|
1709 |
formdata.store() |
|
1710 | ||
1711 |
self.workflow = new_workflow |
|
1712 |
self.store(comment=_('Workflow change')) |
|
1713 |
if formdata_count: |
|
1714 |
# instruct formdef to update its security rules |
|
1715 |
self.data_class().rebuild_security() |
|
1716 | ||
1669 | 1717 | |
1670 | 1718 |
EmailsDirectory.register( |
1671 | 1719 |
'new_user', |
wcs/sql.py | ||
---|---|---|
25 | 25 |
import psycopg2 |
26 | 26 |
import psycopg2.extensions |
27 | 27 |
import psycopg2.extras |
28 |
from psycopg2.sql import SQL, Identifier, Literal |
|
28 | 29 |
import unidecode |
29 | 30 | |
30 | 31 |
try: |
... | ... | |
3669 | 3670 | |
3670 | 3671 |
conn.commit() |
3671 | 3672 |
cur.close() |
3673 | ||
3674 | ||
3675 |
@guard_postgres |
|
3676 |
def formdef_remap_statuses(formdef, mapping): |
|
3677 |
table_name = get_formdef_table_name(formdef) |
|
3678 |
evolutions_table_name = table_name + '_evolutions' |
|
3679 |
unmapped_status_suffix = str(formdef.workflow_id or 'default') |
|
3680 | ||
3681 |
# build the case expression |
|
3682 |
status_cases = [] |
|
3683 |
for old_id, new_id in mapping.items(): |
|
3684 |
status_cases.append( |
|
3685 |
SQL('WHEN status = {old_status} THEN {new_status}').format( |
|
3686 |
old_status=Literal(old_id), new_status=Literal(new_id) |
|
3687 |
) |
|
3688 |
) |
|
3689 |
case_expression = SQL( |
|
3690 |
'(CASE WHEN status IS NULL THEN NULL ' |
|
3691 |
'{status_cases} ' |
|
3692 |
# keep status alread marked as invalid |
|
3693 |
'WHEN status LIKE {pattern} THEN status ' |
|
3694 |
# mark unknown statuses as invalid |
|
3695 |
'ELSE (status || {suffix}) END)' |
|
3696 |
).format( |
|
3697 |
status_cases=SQL('').join(status_cases), |
|
3698 |
pattern=Literal('%-invalid-%'), |
|
3699 |
suffix=Literal('-invalid-' + unmapped_status_suffix), |
|
3700 |
) |
|
3701 | ||
3702 |
conn, cur = get_connection_and_cursor() |
|
3703 |
# update formdatas statuses |
|
3704 |
cur.execute( |
|
3705 |
SQL('UPDATE {table_name} SET status = {case_expression} WHERE status <> {draft_status}').format( |
|
3706 |
table_name=Identifier(table_name), case_expression=case_expression, draft_status=Literal('draft') |
|
3707 |
) |
|
3708 |
) |
|
3709 |
# update evolutions statuses |
|
3710 |
cur.execute( |
|
3711 |
SQL('UPDATE {table_name} SET status = {case_expression}').format( |
|
3712 |
table_name=Identifier(evolutions_table_name), case_expression=case_expression |
|
3713 |
) |
|
3714 |
) |
|
3715 |
conn.commit() |
|
3716 |
cur.close() |
|
3672 |
- |