Projet

Général

Profil

0001-general-add-handling-of-criticality-levels-10134.patch

Frédéric Péters, 10 mars 2016 16:32

Télécharger (53,7 ko)

Voir les différences:

Subject: [PATCH] general: add handling of criticality levels (#10134)

 tests/test_admin_pages.py           |  62 +++++++++++++++++
 tests/test_backoffice_pages.py      | 122 +++++++++++++++++++++++++++++++++-
 tests/test_formdata.py              |  67 +++++++++++++++++++
 tests/test_sql.py                   |  56 +++++++++++++++-
 tests/test_workflow_import.py       |  15 ++++-
 tests/test_workflows.py             |  49 +++++++++++++-
 tests/utilities.py                  |   1 +
 wcs/admin/workflows.py              | 129 ++++++++++++++++++++++++++++++++++--
 wcs/backoffice/management.py        |  54 ++++++++++++++-
 wcs/formdata.py                     |  51 ++++++++++++++
 wcs/formdef.py                      |   2 +-
 wcs/forms/backoffice.py             |  24 ++++++-
 wcs/forms/common.py                 |  14 ----
 wcs/qommon/static/css/dc2/admin.css |  34 ++++++++++
 wcs/qommon/static/js/biglist.js     |   1 +
 wcs/sql.py                          |  26 +++++++-
 wcs/wf/criticality.py               |  77 +++++++++++++++++++++
 wcs/workflows.py                    |  52 +++++++++++++--
 18 files changed, 802 insertions(+), 34 deletions(-)
 create mode 100644 wcs/wf/criticality.py
tests/test_admin_pages.py
1378 1378
    assert Workflow.count() == 0
1379 1379

  
1380 1380
def test_workflows_add_all_actions(pub):
1381
    create_superuser(pub)
1382

  
1381 1383
    Workflow.wipe()
1382 1384
    workflow = Workflow(name='foo')
1383 1385
    workflow.add_status(name='baz')
......
1705 1707
    resp = resp.form.submit('submit')
1706 1708
    assert Workflow.get(workflow.id).global_actions[0].triggers[0].roles == ['_receiver']
1707 1709

  
1710
def test_workflows_criticality_levels(pub):
1711
    create_superuser(pub)
1712
    create_role()
1713

  
1714
    Workflow.wipe()
1715
    workflow = Workflow(name='foo')
1716
    workflow.store()
1717

  
1718
    app = login(get_app(pub))
1719
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
1720
    resp = resp.click('add criticality level')
1721
    resp = resp.forms[0].submit('cancel')
1722
    assert not Workflow.get(workflow.id).criticality_levels
1723

  
1724
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
1725
    resp = resp.click('add criticality level')
1726
    resp.forms[0]['name'] = 'vigilance'
1727
    resp = resp.forms[0].submit('submit')
1728
    assert len(Workflow.get(workflow.id).criticality_levels) == 1
1729
    assert Workflow.get(workflow.id).criticality_levels[0].name == 'vigilance'
1730

  
1731
    # test rename
1732
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
1733
    resp = resp.click('vigilance')
1734
    resp = resp.forms[0].submit('cancel')
1735

  
1736
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
1737
    resp = resp.click('vigilance')
1738
    resp.forms[0]['name'] = 'Vigilance'
1739
    resp = resp.forms[0].submit('submit')
1740
    assert len(Workflow.get(workflow.id).criticality_levels) == 1
1741
    assert Workflow.get(workflow.id).criticality_levels[0].name == 'Vigilance'
1742

  
1743
    # add a second level
1744
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
1745
    resp = resp.click('add criticality level')
1746
    resp.forms[0]['name'] = 'Alerte attentat'
1747
    resp = resp.forms[0].submit('submit')
1748
    assert len(Workflow.get(workflow.id).criticality_levels) == 2
1749
    assert Workflow.get(workflow.id).criticality_levels[0].name == 'Vigilance'
1750
    assert Workflow.get(workflow.id).criticality_levels[1].name == 'Alerte attentat'
1751

  
1752
    # test reorder
1753
    level1_id = Workflow.get(workflow.id).criticality_levels[0].id
1754
    level2_id = Workflow.get(workflow.id).criticality_levels[1].id
1755
    app.get('/backoffice/workflows/%s/update_criticality_levels_order?order=%s;%s;' % (
1756
            workflow.id, level1_id, level2_id))
1757
    assert Workflow.get(workflow.id).criticality_levels[0].id == level1_id
1758
    assert Workflow.get(workflow.id).criticality_levels[1].id == level2_id
1759
    app.get('/backoffice/workflows/%s/update_criticality_levels_order?order=%s;%s;' % (
1760
            workflow.id, level2_id, level1_id))
1761
    assert Workflow.get(workflow.id).criticality_levels[0].id == level2_id
1762
    assert Workflow.get(workflow.id).criticality_levels[1].id == level1_id
1763

  
1764
    # test removal
1765
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
1766
    resp = resp.click('Vigilance')
1767
    resp = resp.forms[0].submit('delete-level')
1768
    assert len(Workflow.get(workflow.id).criticality_levels) == 1
1769

  
1708 1770
def test_workflows_wscall_label(pub):
1709 1771
    create_superuser(pub)
1710 1772
    create_role()
tests/test_backoffice_pages.py
18 18
from wcs.roles import Role
19 19
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem,
20 20
        ChoiceWorkflowStatusItem, EditableWorkflowStatusItem,
21
        JumpOnSubmitWorkflowStatusItem)
21
        JumpOnSubmitWorkflowStatusItem, WorkflowCriticalityLevel)
22 22
from wcs.wf.dispatch import DispatchWorkflowStatusItem
23 23
from wcs.wf.wscall import WebserviceCallStatusItem
24 24
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
......
1835 1835
    assert resp.form['f2'].value == 'XXX'
1836 1836
    assert 'Original form' in resp.body
1837 1837
    assert formdata.get_url(backoffice=True) in resp.body
1838

  
1839
def test_backoffice_criticality_in_formdef_listing(pub):
1840
    if not pub.is_using_postgresql():
1841
        pytest.skip('this requires SQL')
1842
        return
1843
    user = create_user(pub)
1844
    create_environment(pub)
1845

  
1846
    wf = Workflow.get_default_workflow()
1847
    wf.id = '2'
1848
    wf.criticality_levels = [
1849
        WorkflowCriticalityLevel(name='green'),
1850
        WorkflowCriticalityLevel(name='yellow'),
1851
        WorkflowCriticalityLevel(name='red'),
1852
    ]
1853
    wf.store()
1854
    formdef = FormDef.get_by_urlname('form-title')
1855
    formdef.workflow_id = wf.id
1856
    formdef.store()
1857

  
1858
    formdata1, formdata2, formdata3, formdata4 = [
1859
            x for x in formdef.data_class().select() if x.status == 'wf-new'][:4]
1860

  
1861
    formdata1.set_criticality_level(1)
1862
    formdata1.store()
1863
    formdata1_str = '>%s<' % formdata1.get_display_id()
1864
    formdata2.set_criticality_level(2)
1865
    formdata2.store()
1866
    formdata2_str = '>%s<' % formdata2.get_display_id()
1867
    formdata3.set_criticality_level(2)
1868
    formdata3.store()
1869
    formdata3_str = '>%s<' % formdata3.get_display_id()
1870
    formdata4_str = '>%s<' % formdata4.get_display_id()
1871

  
1872
    app = login(get_app(pub))
1873
    resp = app.get('/backoffice/management/form-title/?order_by=-criticality_level&limit=100')
1874
    assert resp.body.index(formdata1_str) > resp.body.index(formdata2_str)
1875
    assert resp.body.index(formdata1_str) > resp.body.index(formdata3_str)
1876
    assert resp.body.index(formdata1_str) < resp.body.index(formdata4_str)
1877

  
1878
    resp = app.get('/backoffice/management/form-title/?order_by=criticality_level&limit=100')
1879
    assert resp.body.index(formdata1_str) < resp.body.index(formdata2_str)
1880
    assert resp.body.index(formdata1_str) < resp.body.index(formdata3_str)
1881
    assert resp.body.index(formdata1_str) > resp.body.index(formdata4_str)
1882

  
1883
def test_backoffice_criticality_in_global_listing(pub):
1884
    if not pub.is_using_postgresql():
1885
        pytest.skip('this requires SQL')
1886
        return
1887

  
1888
    user = create_user(pub)
1889
    create_environment(pub)
1890

  
1891
    wf = Workflow.get_default_workflow()
1892
    wf.id = '2'
1893
    wf.criticality_levels = [
1894
        WorkflowCriticalityLevel(name='green'),
1895
        WorkflowCriticalityLevel(name='yellow'),
1896
        WorkflowCriticalityLevel(name='red'),
1897
    ]
1898
    wf.store()
1899
    formdef = FormDef.get_by_urlname('form-title')
1900
    formdef.workflow_id = wf.id
1901
    formdef.store()
1902

  
1903
    formdata1, formdata2, formdata3, formdata4 = [
1904
            x for x in formdef.data_class().select() if x.status == 'wf-new'][:4]
1905

  
1906
    formdata1.set_criticality_level(1)
1907
    formdata1.store()
1908
    formdata1_str = '>%s<' % formdata1.get_display_id()
1909
    formdata2.set_criticality_level(2)
1910
    formdata2.store()
1911
    formdata2_str = '>%s<' % formdata2.get_display_id()
1912
    formdata3.set_criticality_level(2)
1913
    formdata3.store()
1914
    formdata3_str = '>%s<' % formdata3.get_display_id()
1915
    formdata4_str = '>%s<' % formdata4.get_display_id()
1916

  
1917
    app = login(get_app(pub))
1918
    resp = app.get('/backoffice/management/listing?order_by=-criticality_level&limit=100')
1919
    assert resp.body.index(formdata1_str) > resp.body.index(formdata2_str)
1920
    assert resp.body.index(formdata1_str) > resp.body.index(formdata3_str)
1921
    assert resp.body.index(formdata1_str) < resp.body.index(formdata4_str)
1922

  
1923
    resp = app.get('/backoffice/management/listing?order_by=criticality_level&limit=100')
1924
    assert resp.body.index(formdata1_str) < resp.body.index(formdata2_str)
1925
    assert resp.body.index(formdata1_str) < resp.body.index(formdata3_str)
1926
    assert resp.body.index(formdata1_str) > resp.body.index(formdata4_str)
1927

  
1928
def test_backoffice_criticality_formdata_view(pub):
1929
    user = create_user(pub)
1930
    create_environment(pub)
1931

  
1932
    wf = Workflow.get_default_workflow()
1933
    wf.id = '2'
1934
    wf.criticality_levels = [
1935
        WorkflowCriticalityLevel(name='green'),
1936
        WorkflowCriticalityLevel(name='yellow'),
1937
        WorkflowCriticalityLevel(name='red'),
1938
    ]
1939
    wf.store()
1940
    formdef = FormDef.get_by_urlname('form-title')
1941
    formdef.workflow_id = wf.id
1942
    formdef.store()
1943

  
1944
    formdef = FormDef.get_by_urlname('form-title')
1945
    formdata = [x for x in formdef.data_class().select() if x.status == 'wf-new'][0]
1946

  
1947
    formdata.set_criticality_level(1)
1948
    formdata.store()
1949

  
1950
    app = login(get_app(pub))
1951
    resp = app.get(formdata.get_url(backoffice=True))
1952
    assert 'Criticality Level: yellow' in resp.body
1953

  
1954
    formdata.set_criticality_level(2)
1955
    formdata.store()
1956
    resp = app.get(formdata.get_url(backoffice=True))
1957
    assert 'Criticality Level: red' in resp.body
tests/test_formdata.py
10 10
from wcs import fields, formdef
11 11
from wcs.formdef import FormDef
12 12
from wcs.formdata import Evolution
13
from wcs.workflows import Workflow, WorkflowCriticalityLevel
14
from wcs.wf.anonymise import AnonymiseWorkflowStatusItem
13 15

  
14 16
from utilities import create_temporary_pub, clean_temporary_pub
15 17

  
......
240 242
    clean_drafts(pub)
241 243
    assert formdef.data_class().count() == 1
242 244
    assert formdef.data_class().select()[0].id == d_id1
245

  
246
def test_criticality_levels(pub):
247
    workflow = Workflow(name='criticality')
248
    workflow.criticality_levels = [
249
        WorkflowCriticalityLevel(name='green'),
250
        WorkflowCriticalityLevel(name='yellow'),
251
        WorkflowCriticalityLevel(name='red'),
252
    ]
253
    workflow.store()
254

  
255
    formdef = FormDef()
256
    formdef.name = 'foo'
257
    formdef.fields = []
258
    formdef.workflow_id = workflow.id
259
    formdef.store()
260
    formdef.data_class().wipe()
261

  
262
    d = formdef.data_class()()
263
    assert d.get_criticality_level_object().name == 'green'
264
    d.increase_criticality_level()
265
    assert d.get_criticality_level_object().name == 'yellow'
266
    d.increase_criticality_level()
267
    assert d.get_criticality_level_object().name == 'red'
268
    d.increase_criticality_level()
269
    assert d.get_criticality_level_object().name == 'red'
270
    d.decrease_criticality_level()
271
    assert d.get_criticality_level_object().name == 'yellow'
272
    d.decrease_criticality_level()
273
    assert d.get_criticality_level_object().name == 'green'
274
    d.decrease_criticality_level()
275
    assert d.get_criticality_level_object().name == 'green'
276
    d.set_criticality_level(1)
277
    assert d.get_criticality_level_object().name == 'yellow'
278
    d.set_criticality_level(2)
279
    assert d.get_criticality_level_object().name == 'red'
280
    d.set_criticality_level(4)
281
    assert d.get_criticality_level_object().name == 'red'
282

  
283
    workflow.criticality_levels = [WorkflowCriticalityLevel(name='green')]
284
    workflow.store()
285
    formdef = FormDef.get(id=formdef.id) # reload formdef
286
    d = formdef.data_class()()
287
    assert d.get_criticality_level_object().name == 'green'
288
    d.increase_criticality_level()
289
    assert d.get_criticality_level_object().name == 'green'
290

  
291
    workflow.criticality_levels = [
292
        WorkflowCriticalityLevel(name='green'),
293
        WorkflowCriticalityLevel(name='yellow'),
294
    ]
295
    workflow.store()
296
    formdef = FormDef.get(id=formdef.id) # reload formdef
297
    d = formdef.data_class()()
298
    d.criticality_level = 104
299
    # set too high, this simulates a workflow being changed to have less
300
    # levels than before.
301
    assert d.get_criticality_level_object().name == 'yellow'
302
    d.increase_criticality_level()
303
    assert d.get_criticality_level_object().name == 'yellow'
304
    d.decrease_criticality_level()
305
    assert d.get_criticality_level_object().name == 'green'
306

  
307
    d.criticality_level = 104
308
    d.decrease_criticality_level()
309
    assert d.get_criticality_level_object().name == 'green'
tests/test_sql.py
10 10
from wcs import formdef, publisher, fields
11 11
from wcs.formdef import FormDef
12 12
from wcs.formdata import Evolution
13
from wcs.workflows import Workflow, CommentableWorkflowStatusItem
13
from wcs.workflows import Workflow, CommentableWorkflowStatusItem, WorkflowCriticalityLevel
14 14
from wcs import sql
15 15
import wcs.qommon.storage as st
16 16

  
......
1351 1351
    assert sql.get_yearly_totals() == [(str(datetime.date.today().year), 2)]
1352 1352
    sql.refresh_materialized_views()
1353 1353
    assert sql.get_yearly_totals() == [(str(datetime.date.today().year), 3)]
1354

  
1355
@postgresql
1356
def test_criticality_levels():
1357
    drop_formdef_tables()
1358
    conn, cur = sql.get_connection_and_cursor()
1359

  
1360
    workflow1 = Workflow(name='criticality1')
1361
    workflow1.criticality_levels = [
1362
        WorkflowCriticalityLevel(name='green'),
1363
        WorkflowCriticalityLevel(name='yellow'),
1364
        WorkflowCriticalityLevel(name='red'),
1365
        WorkflowCriticalityLevel(name='redder'),
1366
        WorkflowCriticalityLevel(name='reddest'),
1367
    ]
1368
    workflow1.store()
1369

  
1370
    workflow2 = Workflow(name='criticality2')
1371
    workflow2.criticality_levels = [
1372
        WorkflowCriticalityLevel(name='green'),
1373
        WorkflowCriticalityLevel(name='reddest'),
1374
    ]
1375
    workflow2.store()
1376

  
1377
    formdef1 = FormDef()
1378
    formdef1.name = 'test criticality levels 1'
1379
    formdef1.fields = []
1380
    formdef1.workflow_id = workflow1.id
1381
    formdef1.store()
1382

  
1383
    formdef2 = FormDef()
1384
    formdef2.name = 'test criticality levels 2'
1385
    formdef2.fields = []
1386
    formdef2.workflow_id = workflow2.id
1387
    formdef2.store()
1388

  
1389
    data_class = formdef1.data_class(mode='sql')
1390
    for i in range(5):
1391
        formdata = data_class()
1392
        formdata.set_criticality_level(i)
1393
        formdata.store()
1394

  
1395
    data_class = formdef2.data_class(mode='sql')
1396
    for i in range(2):
1397
        formdata = data_class()
1398
        formdata.set_criticality_level(i)
1399
        formdata.store()
1400

  
1401
    objects = sql.AnyFormData.select(order_by='-criticality_level')
1402
    # make sure first two formdata are the highest priority ones, and the last
1403
    # two formdata are the lowest priority ones.
1404
    assert objects[0].get_criticality_level_object().name == 'reddest'
1405
    assert objects[1].get_criticality_level_object().name == 'reddest'
1406
    assert objects[-1].get_criticality_level_object().name == 'green'
1407
    assert objects[-2].get_criticality_level_object().name == 'green'
tests/test_workflow_import.py
6 6
from quixote import cleanup
7 7
from wcs import publisher
8 8

  
9
from wcs.workflows import Workflow, CommentableWorkflowStatusItem
9
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem,
10
        WorkflowCriticalityLevel)
10 11
from wcs.wf.wscall import WebserviceCallStatusItem
11 12
from wcs.wf.dispatch import DispatchWorkflowStatusItem
12 13
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
......
426 427
        assert role_id in wf2.possible_status[0].items[0].to
427 428

  
428 429
    wf2 = assert_import_export_works(wf, include_id=True)
430

  
431
def test_criticality_level():
432
    wf = Workflow(name='criticality level')
433
    wf.criticality_levels = [
434
        WorkflowCriticalityLevel(name='green'),
435
        WorkflowCriticalityLevel(name='yellow'),
436
        WorkflowCriticalityLevel(name='red', colour='FF0000'),
437
    ]
438

  
439
    wf2 = assert_import_export_works(wf)
440
    assert wf2.criticality_levels[0].name == 'green'
441
    assert wf2.criticality_levels[1].name == 'yellow'
tests/test_workflows.py
14 14
from wcs.workflows import (Workflow, WorkflowStatusItem,
15 15
        SendmailWorkflowStatusItem, SendSMSWorkflowStatusItem,
16 16
        DisplayMessageWorkflowStatusItem,
17
        AbortActionException)
17
        AbortActionException, WorkflowCriticalityLevel)
18 18
from wcs.wf.anonymise import AnonymiseWorkflowStatusItem
19
from wcs.wf.criticality import ModifyCriticalityWorkflowStatusItem, MODE_INC, MODE_DEC, MODE_SET
19 20
from wcs.wf.dispatch import DispatchWorkflowStatusItem
20 21
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
21 22
from wcs.wf.jump import JumpWorkflowStatusItem, _apply_timeouts
......
943 944
    assert emails.get('Foobar')
944 945
    assert set(emails.get('Foobar')['email_rcpt']) == set(
945 946
            ['foo@localhost', 'bar@localhost', 'baz@localhost'])
947

  
948
def test_criticality(pub):
949
    FormDef.wipe()
950

  
951
    workflow = Workflow(name='criticality')
952
    workflow.criticality_levels = [
953
        WorkflowCriticalityLevel(name='green'),
954
        WorkflowCriticalityLevel(name='yellow'),
955
        WorkflowCriticalityLevel(name='red'),
956
    ]
957
    workflow.store()
958

  
959
    formdef = FormDef()
960
    formdef.name = 'baz'
961
    formdef.workflow_id = workflow.id
962
    formdef.store()
963

  
964
    item = ModifyCriticalityWorkflowStatusItem()
965

  
966
    formdata = formdef.data_class()()
967
    item.perform(formdata)
968
    assert not formdata.criticality_level
969

  
970
    item.mode = MODE_INC
971
    item.perform(formdata)
972
    assert formdata.get_criticality_level_object().name == 'yellow'
973
    item.perform(formdata)
974
    assert formdata.get_criticality_level_object().name == 'red'
975
    item.perform(formdata)
976
    assert formdata.get_criticality_level_object().name == 'red'
977

  
978
    item.mode = MODE_DEC
979
    item.perform(formdata)
980
    assert formdata.get_criticality_level_object().name == 'yellow'
981
    item.perform(formdata)
982
    assert formdata.get_criticality_level_object().name == 'green'
983
    item.perform(formdata)
984
    assert formdata.get_criticality_level_object().name == 'green'
985

  
986
    item.mode = MODE_SET
987
    item.absolute_value = 2
988
    item.perform(formdata)
989
    assert formdata.get_criticality_level_object().name == 'red'
990
    item.absolute_value = 0
991
    item.perform(formdata)
992
    assert formdata.get_criticality_level_object().name == 'green'
tests/utilities.py
99 99
    fd.write('formdef-captcha-option = true\n')
100 100
    fd.write('workflow-resubmit-action = true\n')
101 101
    fd.write('workflow-global-actions = true\n')
102
    fd.write('workflow-criticality-levels = true\n')
102 103
    if sql_mode:
103 104
        fd.write('postgresql = true\n')
104 105
        conn = psycopg2.connect(user=os.environ['USER'])
wcs/admin/workflows.py
949 949
        return redirect('..')
950 950

  
951 951

  
952
class CriticalityLevelsDirectory(Directory):
953
    _q_exports = ['', 'new']
954

  
955
    def __init__(self, workflow):
956
        self.workflow = workflow
957

  
958
    def _q_traverse(self, path):
959
        get_response().breadcrumb.append(
960
                ('criticality-levels/', _('Criticality Levels')))
961
        return Directory._q_traverse(self, path)
962

  
963
    def new(self):
964
        currentlevels = self.workflow.criticality_levels or []
965
        default_colours = ['FFFFFF', 'FFFF00', 'FF9900', 'FF6600', 'FF0000']
966
        try:
967
            default_colour = default_colours[len(currentlevels)]
968
        except IndexError:
969
            default_colour = '000000'
970
        form = Form(enctype='multipart/form-data')
971
        form.add(StringWidget, 'name', title=_('Name'), required=True, size=50)
972
        form.add(ColourWidget, 'colour', title=_('Colour'), required=False,
973
                value=default_colour)
974
        form.add_submit('submit', _('Add'))
975
        form.add_submit('cancel', _('Cancel'))
976
        if form.get_widget('cancel').parse():
977
            return redirect('..')
978

  
979
        if form.is_submitted() and not form.has_errors():
980
            if not self.workflow.criticality_levels:
981
                self.workflow.criticality_levels = []
982
            level = WorkflowCriticalityLevel()
983
            level.name = form.get_widget('name').parse()
984
            level.colour = form.get_widget('colour').parse()
985
            self.workflow.criticality_levels.append(level)
986
            self.workflow.store()
987
            return redirect('..')
988

  
989
        get_response().breadcrumb.append(('new', _('New Criticality Level')))
990
        html_top('workflows', title=_('New Criticality level'))
991
        r = TemplateIO(html=True)
992
        r += htmltext('<h2>%s</h2>') % _('New Criticality Level')
993
        r += form.render()
994
        return r.getvalue()
995

  
996
    def _q_lookup(self, component):
997
        for level in (self.workflow.criticality_levels or []):
998
            if level.id == component:
999
                break
1000
        else:
1001
            raise errors.TraversalError()
1002

  
1003
        form = Form(enctype='multipart/form-data')
1004
        form.add(StringWidget, 'name', title=_('Name'), required=True, size=50,
1005
                value=level.name)
1006
        form.add(ColourWidget, 'colour', title=_('Colour'), required=False,
1007
                value=level.colour)
1008
        form.add_submit('submit', _('Submit'))
1009
        form.add_submit('cancel', _('Cancel'))
1010
        form.add_submit('delete-level', _('Delete'))
1011

  
1012
        if form.get_widget('cancel').parse():
1013
            return redirect('..')
1014

  
1015
        if form.get_submit() == 'delete-level':
1016
            self.workflow.criticality_levels.remove(level)
1017
            self.workflow.store()
1018
            return redirect('..')
1019

  
1020
        if form.is_submitted() and not form.has_errors():
1021
            level.name = form.get_widget('name').parse()
1022
            level.colour = form.get_widget('colour').parse()
1023
            self.workflow.store()
1024
            return redirect('..')
1025

  
1026
        get_response().breadcrumb.append(('new', _('Edit Criticality Level')))
1027
        html_top('workflows', title=_('Edit Criticality Level'))
1028
        r = TemplateIO(html=True)
1029
        r += htmltext('<h2>%s</h2>') % _('Edit Criticality Level')
1030
        r += form.render()
1031
        return r.getvalue()
1032

  
1033
    def _q_index(self):
1034
        return redirect('..')
1035

  
1036

  
952 1037
class GlobalActionPage(WorkflowStatusPage):
953 1038
    _q_exports = ['', 'new', 'delete', 'newitem', ('items', 'items_dir'),
954 1039
                  'edit', ('triggers', 'triggers_dir'),
......
1139 1224
class WorkflowPage(Directory):
1140 1225
    _q_exports = ['', 'edit', 'delete', 'newstatus', ('status', 'status_dir'), 'update_order',
1141 1226
            'duplicate', 'export', 'svg', ('variables', 'variables_dir'),
1142
            'update_actions_order',
1143
            ('functions', 'functions_dir'), ('global-actions', 'global_actions_dir')]
1227
            'update_actions_order', 'update_criticality_levels_order',
1228
            ('functions', 'functions_dir'), ('global-actions', 'global_actions_dir'),
1229
            ('criticality-levels', 'criticality_levels_dir'),
1230
            ]
1144 1231

  
1145 1232
    def __init__(self, component, html_top):
1146 1233
        try:
......
1153 1240
        self.variables_dir = VariablesDirectory(self.workflow)
1154 1241
        self.functions_dir = FunctionsDirectory(self.workflow)
1155 1242
        self.global_actions_dir = GlobalActionsDirectory(self.workflow, html_top)
1243
        self.criticality_levels_dir = CriticalityLevelsDirectory(self.workflow)
1156 1244
        get_response().breadcrumb.append((component + '/', self.workflow.name))
1157 1245

  
1158 1246
    def _q_index(self):
1159 1247
        self.html_top(title = _('Workflow - %s') % self.workflow.name)
1160 1248
        r = TemplateIO(html=True)
1161
        get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'svg-pan-zoom.js'])
1249
        get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js',
1250
            'svg-pan-zoom.js', 'jquery.colourpicker.js'])
1162 1251
        get_response().add_javascript_code('$(function () { svgPanZoom("svg", {controlIconsEnabled: true}); });')
1163 1252
        r += htmltext('<h2>%s - ') % _('Workflow')
1164 1253
        r += htmltext('%s') % self.workflow.name
......
1257 1346
                r += htmltext('<ul id="status-list" class="biglist sortable" '
1258 1347
                              'data-order-function="update_actions_order">')
1259 1348
            else:
1260
                r += htmltext('<ul id="status-list" class="biglist">')
1349
                r += htmltext('<ul class="biglist">')
1261 1350

  
1262 1351
            for action in (self.workflow.global_actions or []):
1263 1352
                r += htmltext('<li class="biglistitem" id="itemId_%s">' % action.id)
......
1270 1359
            r += htmltext('</ul>')
1271 1360
            r += htmltext('</div>')
1272 1361

  
1362
        if not str(self.workflow.id).startswith('_') and get_publisher().has_site_option('workflow-criticality-levels'):
1363
            r += htmltext('<div class="bo-block">')
1364
            r += htmltext('<h3>%s') % _('Criticality Levels')
1365
            if not str(self.workflow.id).startswith('_'):
1366
                r += htmltext(' <span class="change">'
1367
                              '(<a rel="popup" href="criticality-levels/new">'
1368
                              '%s</a>)</span>') % _('add criticality level')
1369
            r += htmltext('</h3>')
1370
            r += htmltext('<ul class="biglist sortable criticality-levels" '
1371
                              'data-order-function="update_criticality_levels_order">')
1372
            for level in (self.workflow.criticality_levels or []):
1373
                style = ''
1374
                if level.colour:
1375
                    style = 'style="border-left-color: #%s"' % level.colour
1376
                r += htmltext('<li class="biglistitem" id="itemId_%s" %s>' % (level.id, style))
1377
                if not str(self.workflow.id).startswith('_'):
1378
                    r += htmltext('<a rel="popup" href="criticality-levels/%s">%s</a>') % (
1379
                            level.id, level.name)
1380
                else:
1381
                    r += htmltext('<a>%s</a>') % level.name
1382
                r += htmltext('</li>')
1383
            r += htmltext('</ul>')
1384
            r += htmltext('</div>')
1385

  
1273 1386
        r += htmltext('</div>') # .splitcontent-right
1274 1387

  
1275 1388
        r += htmltext('<br style="clear:both;"/>')
......
1355 1468
        self.workflow.store()
1356 1469
        return 'ok'
1357 1470

  
1471
    def update_criticality_levels_order(self):
1472
        request = get_request()
1473
        new_order = request.form['order'].strip(';').split(';')
1474
        self.workflow.criticality_levels = [
1475
                [x for x in self.workflow.criticality_levels if x.id == y][0] for y in new_order]
1476
        self.workflow.store()
1477
        return 'ok'
1478

  
1358 1479
    def newstatus(self):
1359 1480
        form = Form(enctype='multipart/form-data', action = 'newstatus')
1360 1481
        form.add(StringWidget, 'name', title = _('Name'), size = 50)
wcs/backoffice/management.py
688 688
                order_by=order_by, limit=limit, offset=offset)
689 689
        include_submission_channel = bool(
690 690
                get_publisher().get_site_option('welco_url', 'variables'))
691
        include_criticality_level = get_publisher().has_site_option('workflow-criticality-levels')
691 692

  
692 693
        r = TemplateIO(html=True)
693 694
        r += htmltext('<table id="listing" class="main">')
694 695
        r += htmltext('<thead>')
695
        r += htmltext('<th></th>') # lock
696
        if include_criticality_level:
697
            r += htmltext('<th data-field-sort-key="criticality_level"><span></span></th>')
698
        else:
699
            r += htmltext('<th></th>') # lock
696 700
        if include_submission_channel:
697 701
            r += htmltext('<th data-field-sort-key="submission_channel"><span>%s</span></th>') % _('Channel')
698 702
        r += htmltext('<th data-field-sort-key="formdef_name"><span>%s</span></th>') % _('Form')
......
713 717
            object_key = 'formdata-%s-%s' % (formdata.formdef.url_name, formdata.id)
714 718
            if object_key in visited_objects:
715 719
                classes.append('advisory-lock')
720
            style = ''
721
            if include_criticality_level:
722
                try:
723
                    level = formdata.get_criticality_level_object()
724
                except IndexError:
725
                    pass
726
                else:
727
                    classes.append('criticality-level')
728
                    style = 'style="border-left-color: #%s;"' % level.colour
716 729
            r += htmltext('<tr class="%s" data-link="%s">' % (
717 730
                    ' '.join(classes),
718 731
                    formdata.get_url(backoffice=True)))
719
            r += htmltext('<td></td>') # lock
732
            r += htmltext('<td %s></td>' % style) # lock
720 733
            if include_submission_channel:
721 734
                r += htmltext('<td>%s</td>') % formdata.get_submission_channel_label()
722 735
            r += htmltext('<td>%s</td>') % formdata.formdef.name
......
1642 1655
        formdata = self.filled
1643 1656

  
1644 1657
        r = TemplateIO(html=True)
1658

  
1659
        if formdata.receipt_time:
1660
            r += htmltext('<div class="extra-context">')
1661
            r += htmltext('<h3>%s</h3>') % _('General Information')
1662
            r += htmltext('<p>')
1663
            tm = misc.localstrftime(formdata.receipt_time)
1664
            r += _('The form has been recorded on %(date)s with the number %(number)s.') % {
1665
                    'date': tm, 'number': formdata.get_display_id()}
1666
            r += htmltext('</p>')
1667
            try:
1668
                status_colour = formdata.get_status().colour
1669
            except AttributeError:
1670
                status_colour = 'ffffff'
1671
            fg_colour = misc.get_foreground_colour(status_colour)
1672

  
1673
            r += htmltext('<p class="current-status"><span class="item" style="background: #%s; color: %s;"></span>' %
1674
                    (status_colour, fg_colour))
1675
            r += htmltext('<span>%s %s</span></p>') % (_('Status:'), formdata.get_status_label())
1676
            if formdata.formdef.workflow.criticality_levels:
1677
                try:
1678
                    level = formdata.get_criticality_level_object()
1679
                except IndexError:
1680
                    pass
1681
                else:
1682
                    r += htmltext('<p class="current-level">')
1683
                    if level.colour:
1684
                        r += htmltext('<span class="item" style="background: #%s;"></span>' % level.colour)
1685
                    r += htmltext('<span>%s %s</span></p>') % (_('Criticality Level:'), level.name)
1686

  
1687
            if formdata.anonymised:
1688
                r += htmltext('<div class="infonotice">')
1689
                r += htmltext(_('This form has been anonymised on %(date)s.')) % {
1690
                        'date': formdata.anonymised.strftime(misc.date_format())}
1691
                r += htmltext('</div>')
1692

  
1693
            r += htmltext('</div>')
1694

  
1645 1695
        if formdata.submission_context or formdata.submission_channel:
1646 1696
            extra_context = formdata.submission_context or {}
1647 1697
            r += htmltext('<div class="extra-context">')
wcs/formdata.py
171 171
    backoffice_submission = False
172 172
    submission_context = None
173 173
    submission_channel = None
174
    criticality_level = 0
174 175

  
175 176
    workflow_data = None
176 177
    workflow_roles = None
......
279 280
        processor.generate(fd, ctx)
280 281
        self.id_display = fd.getvalue()
281 282

  
283
    # criticality levels are stored as [0, 101, 102, 103...], this makes it
284
    # easier to group "uncritical" formdatas (=0) together when sorting.
285
    def get_current_criticality_level(self):
286
        levels = len(self.formdef.workflow.criticality_levels or [0])
287
        current_level = self.criticality_level or 0
288
        if current_level >= 100 + levels:
289
            # too high, probably because the workflow was changed and there is
290
            # fewer levels than before
291
            current_level = 100 + levels - 1
292
        return current_level
293

  
294
    def increase_criticality_level(self):
295
        levels = len(self.formdef.workflow.criticality_levels or [0])
296
        current_level = self.get_current_criticality_level()
297
        if current_level == 0:
298
            current_level = 100
299
        if current_level < (100 + levels - 1):
300
            self.criticality_level = current_level + 1
301
            self.store()
302

  
303
    def decrease_criticality_level(self):
304
        levels = len(self.formdef.workflow.criticality_levels or [0])
305
        current_level = self.get_current_criticality_level()
306
        if current_level == 0:
307
            return
308
        self.criticality_level = current_level - 1
309
        if self.criticality_level <= 100:
310
            self.criticality_level = 0
311
        self.store()
312

  
313
    def set_criticality_level(self, level):
314
        levels = len(self.formdef.workflow.criticality_levels or [0])
315
        level = min(levels-1, level)
316
        if level > 0:
317
            self.criticality_level = 100 + level
318
        else:
319
            self.criticality_level = 0
320
        self.store()
321

  
322
    def get_criticality_level_object(self):
323
        levels = self.formdef.workflow.criticality_levels or []
324
        if not levels:
325
            raise IndexError()
326
        current_level = self.get_current_criticality_level()
327
        if current_level > 0:
328
            current_level = current_level - 100
329
        return levels[current_level]
330

  
282 331
    def perform_workflow(self):
283 332
        url = None
284 333
        get_publisher().substitutions.feed(self)
......
408 457
                'form_url': self.get_url(),
409 458
                'form_url_backoffice': self.get_url(backoffice=True),
410 459
                'form_uri': '%s/%s/' % (self.formdef.url_name, self.id),
460
                'form_criticality_level': self.criticality_level,
411 461
            })
412 462

  
413 463
        d['form_status'] = self.get_status_label()
......
641 691
                'id': data['display_id']}
642 692
        data['receipt_time'] = self.receipt_time
643 693
        data['last_update_time'] = self.last_update_time
694
        data['criticality_level'] = self.criticality_level
644 695
        data['url'] = self.get_url()
645 696

  
646 697
        try:
wcs/formdef.py
290 290
                    rebuild_global_views=True)
291 291
        return t
292 292

  
293
    def update_endpoints(self):
293
    def rebuild_views(self):
294 294
        if get_publisher().is_using_postgresql():
295 295
            import sql
296 296
            sql.do_formdef_tables(self, rebuild_views=True,
wcs/forms/backoffice.py
75 75
            r += htmltext('<col />')
76 76
        r += htmltext('</colgroup>')
77 77

  
78
        include_criticality_level = get_publisher().has_site_option('workflow-criticality-levels')
78 79
        r += htmltext('<thead><tr>')
79
        r += htmltext('<td></td>') # lock
80
        if include_criticality_level:
81
            r += htmltext('<th style="width: 4ex;" data-field-sort-key="criticality_level"><span></span>')
82
        else:
83
            r += htmltext('<td></td>') # lock
80 84
        for f in fields:
81 85
            field_sort_key = None
82 86
            if getattr(f, 'fake', False):
......
209 213
            url_action = ''
210 214
        root_url = get_publisher().get_root_url()
211 215
        visited_objects = get_publisher().get_visited_objects(exclude_user=get_session().user)
216
        include_criticality_level = get_publisher().has_site_option('workflow-criticality-levels')
217
        if include_criticality_level:
218
            include_criticality_level = bool(self.formdef.workflow.criticality_levels)
212 219
        for i, filled in enumerate(items):
213 220
            classes = ['status-%s-%s' % (filled.formdef.workflow.id, filled.status)]
214 221
            if i%2:
......
220 227
            if object_key in visited_objects:
221 228
                classes.append('advisory-lock')
222 229

  
230
            style = ''
231
            if include_criticality_level:
232
                try:
233
                    level = filled.get_criticality_level_object()
234
                except IndexError:
235
                    style = ''
236
                else:
237
                    classes.append('criticality-level')
238
                    style = ' style="border-color: #%s;"' % level.colour
239

  
223 240
            link = str(filled.id) + '/'
224 241
            data = ' data-link="%s"' % link
225 242
            if filled.anonymised:
226 243
                data += ' data-anonymised="true"'
227 244
            r += htmltext('<tr class="%s"%s>' % (' '.join(classes), data))
228
            r += htmltext('<td></td>') # lock
245
            if include_criticality_level:
246
                r += htmltext('<td %s></td>' % style) # criticality_level
247
            else:
248
                r += htmltext('<td></td>') # lock
229 249
            for i, f in enumerate(fields):
230 250
                if f.type == 'id':
231 251
                    r += htmltext('<td class="cell-id"><a href="%s%s">%s</a></td>') % (link, url_action,
wcs/forms/common.py
497 497
        get_logger().info('form %s - id: %s - view status' % (self.formdef.name, self.filled.id))
498 498
        self.html_top('%s - %s' % (self.formdef.name, self.filled.id))
499 499
        r = TemplateIO(html=True)
500
        if self.filled.receipt_time is not None:
501
            tm = misc.localstrftime(self.filled.receipt_time)
502
        else:
503
            tm = '???'
504
        r += htmltext("<p>")
505
        r += _('The form has been recorded on %(date)s with the number %(number)s.') % {
506
                'date': tm, 'number': self.filled.get_display_id()}
507
        r += htmltext("</p>")
508

  
509
        if self.filled.anonymised:
510
            r += htmltext('<div class="infonotice">')
511
            r += htmltext(_('This form has been anonymised on %(date)s.')) % {
512
                    'date': self.filled.anonymised.strftime(misc.date_format())}
513
            r += htmltext('</div>')
514 500

  
515 501
        r += htmltext(self.workflow_messages())
516 502

  
wcs/qommon/static/css/dc2/admin.css
1230 1230
	margin-left: 1em;
1231 1231
}
1232 1232

  
1233
p.current-level span,
1234
p.current-status span {
1235
	line-height: 40px;
1236
}
1237

  
1238
p.current-level .item,
1239
p.current-status .item {
1240
	float: left;
1241
	display: inline-block;
1242
	width: 40px;
1243
	height: 40px;
1244
	border-radius: 20px;
1245
	border: 1px solid #aaa;
1246
	margin-right: 1ex;
1247
	margin-bottom: 1ex;
1248
}
1249

  
1250
p.current-level .item {
1251
	border-radius: 0;
1252
}
1253

  
1254
tr.criticality-level {
1255
	border-left-width: 5px;
1256
	border-left-style: solid;
1257
}
1258

  
1259
div.bo-block ul.biglist.criticality-levels li {
1260
	border-left-width: 5px;
1261
}
1262

  
1263
table#listing tr.criticality-level td:first-child {
1264
	border-left-width: 5px;
1265
	border-left-style: solid;
1266
}
1233 1267
form div.page {
1234 1268
	border: 1px solid #e4e4e4;
1235 1269
	padding: 0;
wcs/qommon/static/js/biglist.js
9 9
            {
10 10
                accept: 'biglistitem',
11 11
                scroll: true,
12
                helper: 'clone',
12 13
                helperclass: 'sortHelper',
13 14
                activeclass :     'sortableactive',
14 15
                hoverclass :     'sortablehover',
wcs/sql.py
325 325
        'anonymised', 'workflow_roles', 'workflow_roles_array',
326 326
        'concerned_roles_array', 'tracking_code',
327 327
        'actions_roles_array', 'backoffice_submission',
328
        'submission_context', 'submission_channel'])
328
        'submission_context', 'submission_channel',
329
        'criticality_level'])
329 330

  
330 331
    # migrations
331 332
    if not 'fts' in existing_fields:
......
360 361
    if not 'submission_channel' in existing_fields:
361 362
        cur.execute('''ALTER TABLE %s ADD COLUMN submission_channel varchar''' % table_name)
362 363

  
364
    if not 'criticality_level' in existing_fields:
365
        cur.execute('''ALTER TABLE %s ADD COLUMN criticality_level integer NOT NULL DEFAULT(0)''' % table_name)
366

  
363 367
    # add new fields
364 368
    for field in formdef.fields:
365 369
        assert field.id is not None
......
614 618
                                ', '.join(["'wf-%s'" % x.id for x in endpoint_status]),
615 619
                        '''is_at_endpoint'''))
616 620

  
621
    # [CRITICALITY_1] Add criticality_level, computed relative to levels in
622
    # the given workflow, so all higher criticalites are sorted first. This is
623
    # reverted when loading the formdata back, in [CRITICALITY_2]
624
    levels = len(formdef.workflow.criticality_levels or [0])
625
    view_fields.append(('''(criticality_level - %d)''' % levels,
626
                        '''criticality_level'''))
627

  
617 628
    view_fields.append(('''(SELECT MAX(time) FROM %s_evolutions '''
618 629
                        ''' WHERE %s.id = %s_evolutions.formdata_id)''' % (
619 630
                            (table_name, )*3),
......
684 695
    common_fields.append(('last_update_time', 'last_update_time'))
685 696
    common_fields.append(('formdef_name', 'formdef_name'))
686 697
    common_fields.append(('user_name', 'user_name'))
698
    common_fields.append(('criticality_level', 'criticality_level'))
687 699

  
688 700
    union = ' UNION '.join(['''SELECT %s FROM %s''' % (
689 701
        ', '.join([y[1] for y in common_fields]), x) for x in view_names])
......
994 1006
        ('backoffice_submission', 'boolean'),
995 1007
        ('submission_context', 'bytea'),
996 1008
        ('submission_channel', 'varchar'),
1009
        ('criticality_level', 'int'),
997 1010
    ]
998 1011

  
999 1012
    def __init__(self, id=None):
......
1088 1101
                'backoffice_submission': self.backoffice_submission,
1089 1102
                'submission_context': self.submission_context,
1090 1103
                'submission_channel': self.submission_channel,
1104
                'criticality_level': self.criticality_level,
1091 1105
        }
1092 1106
        if self.receipt_time:
1093 1107
            sql_dict['receipt_time'] = datetime.datetime.fromtimestamp(time.mktime(self.receipt_time)),
......
1605 1619
        common_fields = get_view_fields(fake_formdef)
1606 1620
        cls.__table_static_fields = [(x[1], x[0]) for x in common_fields]
1607 1621
        cls.__table_static_fields.append(('last_update_time', 'timestamp'))
1622
        cls.__table_static_fields.append(('criticality_level', 'criticality_level'))
1608 1623
        return cls.__table_static_fields
1609 1624

  
1610 1625
    @classmethod
......
1625 1640
        for static_field, value in zip(cls._table_static_fields,
1626 1641
                                       tuple(row[:len(cls._table_static_fields)])):
1627 1642
            setattr(o, static_field[0], value)
1643
        # [CRITICALITY_2] transform criticality_level back to the expected
1644
        # range (see [CRITICALITY_1])
1645
        levels = len(formdef.workflow.criticality_levels or [0])
1646
        o.criticality_level = levels + o.criticality_level
1628 1647
        return o
1629 1648

  
1630 1649

  
......
1786 1805
    return result
1787 1806

  
1788 1807

  
1789
SQL_LEVEL = 13
1808
SQL_LEVEL = 14
1790 1809

  
1791 1810
def migrate_global_views(conn, cur):
1792 1811
    cur.execute('''SELECT COUNT(*) FROM information_schema.tables
......
1820 1839
        raise RuntimeError()
1821 1840
    if sql_level < 1: # 1: introduction of tracking_code table
1822 1841
        do_tracking_code_table()
1823
    if sql_level < 13:
1842
    if sql_level < 14:
1824 1843
        # 2: introduction of formdef_id in views
1825 1844
        # 5: add concerned_roles_array, is_at_endpoint and fts to views
1826 1845
        # 7: add backoffice_submission to tables
......
1829 1848
        # 10: add submission_channel to tables
1830 1849
        # 11: add formdef_name and user_name to views
1831 1850
        # 13: add backoffice_submission to views
1851
        # 14: add criticality_level to tables & views
1832 1852
        migrate_views(conn, cur)
1833 1853
    if sql_level < 12:
1834 1854
        # 3: introduction of _structured for user fields
wcs/wf/criticality.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2016  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
from quixote import get_publisher
18

  
19
from wcs.qommon.form import SingleSelectWidget
20
from wcs.workflows import WorkflowStatusItem, register_item_class
21

  
22
MODE_INC = '1'
23
MODE_DEC = '2'
24
MODE_SET = '3'
25

  
26
class ModifyCriticalityWorkflowStatusItem(WorkflowStatusItem):
27
    description = N_('Modify Criticality')
28
    key = 'modify_criticality'
29

  
30
    mode = None
31
    absolute_value = None
32

  
33
    def get_parameters(self):
34
        return ('mode', 'absolute_value')
35

  
36
    @classmethod
37
    def is_available(cls):
38
        # TODO: if we had per-workflow availability we could show this action
39
        # only on the workflow where criticality levels are set.
40
        return get_publisher().has_site_option('workflow-criticality-levels')
41

  
42
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
43
        if 'mode' in parameters:
44
            form.add(SingleSelectWidget, '%smode' % prefix,
45
                    title=_('Modification Mode'), value=self.mode,
46
                    required=True,
47
                    options=[
48
                        (MODE_INC, _('Increase Level')),
49
                        (MODE_DEC, _('Decrease Level')),
50
                        (MODE_SET, _('Set Level'))],
51
                    attrs={'data-dynamic-display-parent': 'true'})
52
        if 'absolute_value' in parameters:
53
            if self.parent.parent.criticality_levels:
54
                options = [(str(i), x.name) for i, x in enumerate(self.parent.parent.criticality_levels)]
55
            else:
56
                options = [('0', '---')]
57
            form.add(SingleSelectWidget, '%sabsolute_value' % prefix,
58
                     title=_('Value'), value=self.absolute_value,
59
                     options=options,
60
                     attrs={
61
                         'data-dynamic-display-child-of': '%smode' % prefix,
62
                         'data-dynamic-display-value': _('Set Level'),
63
                         }
64
                     )
65

  
66
    def perform(self, formdata):
67
        levels = formdata.formdef.workflow.criticality_levels
68
        current_level = formdata.criticality_level or 0
69
        new_level = current_level
70
        if self.mode == MODE_INC:
71
            formdata.increase_criticality_level()
72
        elif self.mode == MODE_DEC:
73
            formdata.decrease_criticality_level()
74
        elif self.mode == MODE_SET:
75
            formdata.set_criticality_level(self.absolute_value)
76

  
77
register_item_class(ModifyCriticalityWorkflowStatusItem)
wcs/workflows.py
251 251
    roles = None
252 252
    variables_formdef = None
253 253
    global_actions = None
254
    criticality_levels = None
254 255

  
255 256
    last_modification_time = None
256 257
    last_modification_user_id = None
......
261 262
        self.possible_status = []
262 263
        self.roles = {'_receiver': _('Recipient')}
263 264
        self.global_actions = []
265
        self.criticality_levels = []
264 266

  
265 267
    def migrate(self):
266 268
        changed = False
......
279 281
            self.store()
280 282

  
281 283
    def store(self):
282
        must_update_endpoints = False
284
        must_update_views = False
283 285
        if self.id:
284 286
            old_self = self.get(self.id, ignore_errors=True, ignore_migration=True)
285 287
            if old_self:
286 288
                old_endpoints = set([x.id for x in old_self.get_endpoint_status()])
287 289
                if old_endpoints != set([x.id for x in self.get_endpoint_status()]):
288
                    must_update_endpoints = True
290
                    must_update_views = True
291
                old_criticality_levels = len(old_self.criticality_levels or [0])
292
                if old_criticality_levels != len(self.criticality_levels or [0]):
293
                    must_update_views = True
289 294

  
290 295
        self.last_modification_time = time.localtime()
291 296
        if get_request() and get_request().user:
......
298 303
        # their views if endpoints have changed.
299 304
        for form in FormDef.select(lambda x: x.workflow_id == self.id, ignore_migration=True):
300 305
            form.data_class().rebuild_security()
301
            if must_update_endpoints:
302
                form.update_endpoints()
306
            if must_update_views:
307
                form.rebuild_views()
303 308

  
304 309
    def get(cls, id, ignore_errors=False, ignore_migration=False):
305 310
        if id == '_default':
......
452 457
                global_actions.append(action.export_to_xml(charset=charset,
453 458
                    include_id=include_id))
454 459

  
460
        if self.criticality_levels:
461
            criticality_levels = ET.SubElement(root, 'criticality_levels')
462
            for level in self.criticality_levels:
463
                criticality_levels.append(level.export_to_xml(charset=charset))
464

  
455 465
        if self.variables_formdef:
456 466
            variables = ET.SubElement(root, 'variables')
457 467
            formdef = ET.SubElement(variables, 'formdef')
......
516 526
                action_o.init_with_xml(action, charset, include_id=include_id)
517 527
                workflow.global_actions.append(action_o)
518 528

  
529
        workflow.criticality_levels = []
530
        criticality_levels = tree.find('criticality_levels')
531
        if criticality_levels is not None:
532
            for level in criticality_levels:
533
                level_o = WorkflowCriticalityLevel()
534
                level_o.init_with_xml(level, charset)
535
                workflow.criticality_levels.append(level_o)
536

  
519 537
        variables = tree.find('variables')
520 538
        if variables is not None:
521 539
            formdef = variables.find('formdef')
......
941 959
            trigger_o.init_with_xml(trigger, charset, include_id=include_id)
942 960

  
943 961

  
962
class WorkflowCriticalityLevel(object):
963
    id = None
964
    name = None
965
    colour = None
966

  
967
    def __init__(self, name=None, colour=None):
968
        self.name = name
969
        self.colour = colour
970
        self.id = str(random.randint(0, 100000))
971

  
972
    def export_to_xml(self, charset, include_id=False):
973
        level = ET.Element('criticality-level')
974
        ET.SubElement(level, 'id').text = unicode(self.id, charset)
975
        ET.SubElement(level, 'name').text = unicode(self.name, charset)
976
        if self.colour:
977
            ET.SubElement(level, 'colour').text = unicode(self.colour, charset)
978
        return level
979

  
980
    def init_with_xml(self, elem, charset, include_id=False):
981
        self.id = elem.find('id').text.encode(charset)
982
        self.name = elem.find('name').text.encode(charset)
983
        if elem.find('colour') is not None:
984
            self.colour = elem.find('colour').text.encode(charset)
985

  
986

  
944 987
class WorkflowStatus(object):
945 988
    id = None
946 989
    name = None
......
1961 2004
    import wf.anonymise
1962 2005
    import wf.export_to_model
1963 2006
    import wf.resubmit
2007
    import wf.criticality
1964 2008

  
1965 2009
from wf.export_to_model import ExportToModel
1966
-