do not add an evolution on static jump (#22236)

This commit is contained in:
Thomas NOËL 2018-05-01 12:22:19 +02:00
parent 771dec087b
commit ce94b90179
8 changed files with 92 additions and 29 deletions

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import datetime
import json
import pytest
import hashlib
@ -3821,17 +3822,46 @@ def test_form_worklow_multiple_identical_status(pub):
user.roles = [role.id]
user.store()
assert len(formdef.data_class().get(formdata.id).evolution) == 1
assert formdef.data_class().get(formdata.id).evolution[0].last_jump_datetime is None
login(app, username='foo', password='foo')
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
formdata = formdef.data_class().get(formdata.id)
# status is not changed: no new evolution, only a new last_jump_datetime
assert len(formdata.evolution) == 1
assert formdata.status == 'wf-st1'
assert formdata.evolution[0].last_jump_datetime is not None
# add a comment to last evolution, forcing create a new one
formdata.evolution[-1].comment = 'new-evolution-1'
formdata.store()
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
formdata = formdef.data_class().get(formdata.id)
assert len(formdata.evolution) == 2
assert formdata.status == 'wf-st1'
# again
formdata.evolution[-1].comment = 'new-evolution-2'
formdata.store()
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
assert len(formdef.data_class().get(formdata.id).evolution) == 4
assert formdef.data_class().get(formdata.id).status == 'wf-st1'
# last evolution is empty, this last trigger does not create a new one
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302)
# finally, 3 evolutions: new-evolution-1, new-evolution-2, empty
formdata = formdef.data_class().get(formdata.id)
assert len(formdata.evolution) == 3
assert formdata.status == 'wf-st1'
assert formdata.evolution[0].comment == 'new-evolution-1'
assert formdata.evolution[1].comment == 'new-evolution-2'
assert formdata.evolution[2].comment is None
resp = app.get(formdata.get_url())
assert resp.body.count('Status1') == 2 # once in summary and once in journal
assert resp.body.count('CSS-STATUS1') == 1
assert resp.body.count('Status1') == 3 # once in summary and two in journal
assert resp.body.count('CSS-STATUS1') == 2
assert resp.body.count('new-evolution-1') == 1
assert resp.body.count('new-evolution-2') == 1
def test_display_message(pub):
user = create_user(pub)

View File

@ -1456,6 +1456,7 @@ def test_last_update_time():
formdata1 = data_class()
formdata1.status = 'wf-st1'
formdata1.just_created()
formdata1.evolution[0].comment = 'comment'
formdata1.jump_status('st1') # will add another evolution entry
formdata1.evolution[0].time = datetime.datetime(2015, 1, 1, 0, 0, 0).timetuple()
formdata1.evolution[1].time = datetime.datetime(2015, 1, 2, 0, 0, 0).timetuple()
@ -1464,6 +1465,7 @@ def test_last_update_time():
formdata2 = data_class()
formdata2.status = 'wf-st1'
formdata2.just_created()
formdata2.evolution[0].comment = 'comment'
formdata2.jump_status('st1') # will add another evolution entry
formdata2.evolution[0].time = datetime.datetime(2015, 1, 3, 0, 0, 0).timetuple()
formdata2.evolution[1].time = datetime.datetime(2015, 1, 4, 0, 0, 0).timetuple()

View File

@ -1187,9 +1187,7 @@ class FormDefPage(Directory):
if form.get_widget('date').parse():
date = form.get_widget('date').parse()
date = time.strptime(date, misc.date_format())
all_forms = [x for x in all_forms if (
x.evolution and x.evolution[-1].time < date) or (
x.receipt_time < date)]
all_forms = [x for x in all_forms if x.last_update_time < date]
self.fd = StringIO()
t = tarfile.open('wcs.tar.gz', 'w:gz', fileobj=self.fd)

View File

@ -131,6 +131,7 @@ class Evolution(object):
who = None
status = None
time = None
last_jump_datetime = None
comment = None
parts = None
@ -179,6 +180,7 @@ class Evolution(object):
def get_json_export_dict(self, user, anonymise=False):
data = {
'time': self.time,
'last_jump_datetime': self.last_jump_datetime,
}
if self.status:
data['status'] = self.status[3:]
@ -556,13 +558,23 @@ class FormData(StorableObject):
previous_status = self.pop_previous_marked_status()
assert previous_status, 'failed to compute previous status'
status_id = previous_status.id
evo = Evolution(self)
evo.time = time.localtime()
evo.status = 'wf-%s' % status_id
status = 'wf-%s' % status_id
if not self.evolution:
self.evolution = []
elif (self.status == status
and self.evolution[-1].status == status
and not self.evolution[-1].comment
and not self.evolution[-1].display_parts()):
# if status do not change and last evolution is empty,
# just update last jump time on last evolution, do not add one
self.evolution[-1].last_jump_datetime = datetime.datetime.now()
self.store()
return
evo = Evolution(self)
evo.time = time.localtime()
evo.status = status
self.evolution.append(evo)
self.status = evo.status
self.status = status
self.store()
def get_url(self, backoffice = False):
@ -855,7 +867,9 @@ class FormData(StorableObject):
def get_last_update_time(self):
if hasattr(self, '_last_update_time'):
return self._last_update_time
if self.evolution and self.evolution[-1].time:
if self.evolution and self.evolution[-1].last_jump_datetime:
return self.evolution[-1].last_jump_datetime.timetuple()
elif self.evolution and self.evolution[-1].time:
return self.evolution[-1].time
else:
return self.receipt_time

View File

@ -375,6 +375,7 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
who varchar,
status varchar,
time timestamp,
last_jump_datetime timestamp,
comment text,
parts bytea,
formdata_id integer REFERENCES %s (id) ON DELETE CASCADE)''' % (
@ -471,6 +472,15 @@ def do_formdef_tables(formdef, conn=None, cur=None, rebuild_views=False, rebuild
for field in (existing_fields - needed_fields):
cur.execute('''ALTER TABLE %s DROP COLUMN %s CASCADE''' % (table_name, field))
# migrations on _evolutions table
cur.execute('''SELECT column_name FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = '%s_evolutions'
''' % table_name)
evo_existing_fields = set([x[0] for x in cur.fetchall()])
if 'last_jump_datetime' not in evo_existing_fields:
cur.execute('''ALTER TABLE %s_evolutions ADD COLUMN last_jump_datetime timestamp''' % table_name)
if rebuild_views or len(existing_fields - needed_fields):
# views may have been dropped when dropping columns, so we recreate
# them even if not asked to.
@ -1201,7 +1211,8 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
self._evolution = []
return self._evolution
conn, cur = get_connection_and_cursor()
sql_statement = '''SELECT id, who, status, time, comment, parts FROM %s_evolutions
sql_statement = '''SELECT id, who, status, time, last_jump_datetime,
comment, parts FROM %s_evolutions
WHERE formdata_id = %%(id)s
ORDER BY id''' % self._table_name
cur.execute(sql_statement, {'id': self.id})
@ -1218,11 +1229,12 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
@classmethod
def _row2evo(cls, row, formdata):
o = wcs.formdata.Evolution(formdata)
o._sql_id, o.who, o.status, o.time, o.comment = [str_encode(x) for x in tuple(row[:5])]
o._sql_id, o.who, o.status, o.time, o.last_jump_datetime, o.comment = [
str_encode(x) for x in tuple(row[:6])]
if o.time:
o.time = o.time.timetuple()
if row[5]:
o.parts = cPickle.loads(str(row[5]))
if row[6]:
o.parts = cPickle.loads(str(row[6]))
return o
def set_evolution(self, value):
@ -1243,7 +1255,8 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
if not object_dict:
return
conn, cur = get_connection_and_cursor()
sql_statement = '''SELECT id, who, status, time, comment, parts, formdata_id
sql_statement = '''SELECT id, who, status, time, last_jump_datetime,
comment, parts, formdata_id
FROM %s_evolutions''' % cls._table_name
sql_statement += ''' WHERE formdata_id IN %(object_ids)s ORDER BY id'''
cur.execute(sql_statement, {'object_ids': tuple(object_dict.keys())})
@ -1255,7 +1268,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
row = cur.fetchone()
if row is None:
break
_sql_id, who, status, time, comment, parts, formdata_id = tuple(row[:7])
_sql_id, who, status, time, last_jump_datetime, comment, parts, formdata_id = tuple(row[:8])
formdata = object_dict.get(formdata_id)
if not formdata:
continue
@ -1354,6 +1367,7 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
sql_dict.update({'id': evo._sql_id})
sql_statement = '''UPDATE %s_evolutions SET
time = %%(time)s,
last_jump_datetime = %%(last_jump_datetime)s,
status = %%(status)s,
comment = %%(comment)s,
parts = %%(parts)s
@ -1362,16 +1376,19 @@ class SqlFormData(SqlMixin, wcs.formdata.FormData):
else:
sql_statement = '''INSERT INTO %s_evolutions (
id, who, status,
time, comment, parts,
time, last_jump_datetime,
comment, parts,
formdata_id)
VALUES (DEFAULT, %%(who)s, %%(status)s,
%%(time)s, %%(comment)s,
%%(time)s, %%(last_jump_datetime)s,
%%(comment)s,
%%(parts)s, %%(formdata_id)s)
RETURNING id''' % self._table_name
sql_dict.update({
'who': evo.who,
'status': evo.status,
'time': datetime.datetime.fromtimestamp(time.mktime(evo.time)),
'last_jump_datetime': evo.last_jump_datetime,
'comment': evo.comment,
'formdata_id': self.id,
})
@ -2123,7 +2140,7 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None):
return result
SQL_LEVEL = 26
SQL_LEVEL = 27
def migrate_global_views(conn, cur):
cur.execute('''SELECT COUNT(*) FROM information_schema.tables
@ -2201,7 +2218,7 @@ def migrate():
raise RuntimeError()
if sql_level < 1: # 1: introduction of tracking_code table
do_tracking_code_table()
if sql_level < 26:
if sql_level < 27:
# 2: introduction of formdef_id in views
# 5: add concerned_roles_array, is_at_endpoint and fts to views
# 7: add backoffice_submission to tables
@ -2216,6 +2233,7 @@ def migrate():
# 20: remove user hash stuff
# 22: rebuild views
# 26: add digest to formdata
# 27: add last_jump_datetime in evolutions tables
migrate_views(conn, cur)
if sql_level < 21:
# 3: introduction of _structured for user fields

View File

@ -13,7 +13,11 @@
{% if evolution.status %}
<div class="evolution-metadata">
<span class="status">{{evolution.get_status_label}}</span>
<span class="time">{{evolution.datetime}}</span>
<span class="time">{{evolution.datetime}}
{% if evolution.last_jump_datetime and user.is_admin %}
<span class="last-jump">({% trans "last check:" %} {{ evolution.last_jump_datetime }})</span>
{% endif %}
</span>
</div>
{% endif %}
<div class="msg">

View File

@ -243,10 +243,7 @@ class JumpWorkflowStatusItem(WorkflowStatusJumpItem):
if self.timeout:
timeout = int(self.compute(self.timeout))
if formdata.evolution:
last = formdata.evolution[-1].time
else:
last = formdata.receipt_time
last = formdata.last_update_time
if last:
diff = time.time() - time.mktime(last)
must_jump = (diff > timeout) and must_jump

View File

@ -1040,13 +1040,13 @@ class WorkflowGlobalActionTimeoutTrigger(WorkflowGlobalActionTrigger):
anchor_status = self.anchor_status_first or formdata.status
for evolution in formdata.evolution:
if evolution.status == anchor_status:
anchor_date = evolution.time
anchor_date = evolution.last_jump_datetime or evolution.time
break
elif self.anchor == 'latest-arrival':
anchor_status = self.anchor_status_latest or formdata.status
for evolution in reversed(formdata.evolution):
if evolution.status == anchor_status:
anchor_date = evolution.time
anchor_date = evolution.last_jump_datetime or evolution.time
break
elif self.anchor == 'python':
variables = get_publisher().substitutions.get_context_variables()