0001-do-not-add-an-evolution-on-static-jump-22236.patch
tests/test_form_pages.py | ||
---|---|---|
1 | 1 |
# -*- coding: utf-8 -*- |
2 | 2 | |
3 |
import datetime |
|
3 | 4 |
import json |
4 | 5 |
import pytest |
5 | 6 |
import hashlib |
... | ... | |
3821 | 3822 |
user.roles = [role.id] |
3822 | 3823 |
user.store() |
3823 | 3824 | |
3825 |
assert len(formdef.data_class().get(formdata.id).evolution) == 1 |
|
3826 |
assert formdef.data_class().get(formdata.id).evolution[0].last_jump_datetime is None |
|
3827 | ||
3824 | 3828 |
login(app, username='foo', password='foo') |
3825 | 3829 |
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302) |
3830 |
formdata = formdef.data_class().get(formdata.id) |
|
3831 |
# status is not changed: no new evolution, only a new last_jump_datetime |
|
3832 |
assert len(formdata.evolution) == 1 |
|
3833 |
assert formdata.status == 'wf-st1' |
|
3834 |
assert formdata.evolution[0].last_jump_datetime is not None |
|
3835 | ||
3836 |
# add a comment to last evolution, forcing create a new one |
|
3837 |
formdata.evolution[-1].comment = 'new-evolution-1' |
|
3838 |
formdata.store() |
|
3826 | 3839 |
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302) |
3840 |
formdata = formdef.data_class().get(formdata.id) |
|
3841 |
assert len(formdata.evolution) == 2 |
|
3842 |
assert formdata.status == 'wf-st1' |
|
3843 | ||
3844 |
# again |
|
3845 |
formdata.evolution[-1].comment = 'new-evolution-2' |
|
3846 |
formdata.store() |
|
3827 | 3847 |
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302) |
3828 | 3848 | |
3829 |
assert len(formdef.data_class().get(formdata.id).evolution) == 4 |
|
3830 |
assert formdef.data_class().get(formdata.id).status == 'wf-st1' |
|
3849 |
# last evolution is empty, this last trigger does not create a new one |
|
3850 |
resp = app.post(formdata.get_url() + 'jump/trigger/XXX', status=302) |
|
3851 | ||
3852 |
# finally, 3 evolutions: new-evolution-1, new-evolution-2, empty |
|
3853 |
formdata = formdef.data_class().get(formdata.id) |
|
3854 |
assert len(formdata.evolution) == 3 |
|
3855 |
assert formdata.status == 'wf-st1' |
|
3856 |
assert formdata.evolution[0].comment == 'new-evolution-1' |
|
3857 |
assert formdata.evolution[1].comment == 'new-evolution-2' |
|
3858 |
assert formdata.evolution[2].comment is None |
|
3831 | 3859 | |
3832 | 3860 |
resp = app.get(formdata.get_url()) |
3833 |
assert resp.body.count('Status1') == 2 # once in summary and once in journal |
|
3834 |
assert resp.body.count('CSS-STATUS1') == 1 |
|
3861 |
assert resp.body.count('Status1') == 3 # once in summary and two in journal |
|
3862 |
assert resp.body.count('CSS-STATUS1') == 2 |
|
3863 |
assert resp.body.count('new-evolution-1') == 1 |
|
3864 |
assert resp.body.count('new-evolution-2') == 1 |
|
3835 | 3865 | |
3836 | 3866 |
def test_display_message(pub): |
3837 | 3867 |
user = create_user(pub) |
tests/test_sql.py | ||
---|---|---|
1456 | 1456 |
formdata1 = data_class() |
1457 | 1457 |
formdata1.status = 'wf-st1' |
1458 | 1458 |
formdata1.just_created() |
1459 |
formdata1.evolution[0].comment = 'comment' |
|
1459 | 1460 |
formdata1.jump_status('st1') # will add another evolution entry |
1460 | 1461 |
formdata1.evolution[0].time = datetime.datetime(2015, 1, 1, 0, 0, 0).timetuple() |
1461 | 1462 |
formdata1.evolution[1].time = datetime.datetime(2015, 1, 2, 0, 0, 0).timetuple() |
... | ... | |
1464 | 1465 |
formdata2 = data_class() |
1465 | 1466 |
formdata2.status = 'wf-st1' |
1466 | 1467 |
formdata2.just_created() |
1468 |
formdata2.evolution[0].comment = 'comment' |
|
1467 | 1469 |
formdata2.jump_status('st1') # will add another evolution entry |
1468 | 1470 |
formdata2.evolution[0].time = datetime.datetime(2015, 1, 3, 0, 0, 0).timetuple() |
1469 | 1471 |
formdata2.evolution[1].time = datetime.datetime(2015, 1, 4, 0, 0, 0).timetuple() |
wcs/admin/forms.py | ||
---|---|---|
1187 | 1187 |
if form.get_widget('date').parse(): |
1188 | 1188 |
date = form.get_widget('date').parse() |
1189 | 1189 |
date = time.strptime(date, misc.date_format()) |
1190 |
all_forms = [x for x in all_forms if ( |
|
1191 |
x.evolution and x.evolution[-1].time < date) or ( |
|
1192 |
x.receipt_time < date)] |
|
1190 |
all_forms = [x for x in all_forms if x.last_update_time < date] |
|
1193 | 1191 | |
1194 | 1192 |
self.fd = StringIO() |
1195 | 1193 |
t = tarfile.open('wcs.tar.gz', 'w:gz', fileobj=self.fd) |
wcs/formdata.py | ||
---|---|---|
131 | 131 |
who = None |
132 | 132 |
status = None |
133 | 133 |
time = None |
134 |
last_jump_datetime = None |
|
134 | 135 |
comment = None |
135 | 136 |
parts = None |
136 | 137 | |
... | ... | |
179 | 180 |
def get_json_export_dict(self, user, anonymise=False): |
180 | 181 |
data = { |
181 | 182 |
'time': self.time, |
183 |
'last_jump_datetime': self.last_jump_datetime, |
|
182 | 184 |
} |
183 | 185 |
if self.status: |
184 | 186 |
data['status'] = self.status[3:] |
... | ... | |
556 | 558 |
previous_status = self.pop_previous_marked_status() |
557 | 559 |
assert previous_status, 'failed to compute previous status' |
558 | 560 |
status_id = previous_status.id |
559 |
evo = Evolution(self) |
|
560 |
evo.time = time.localtime() |
|
561 |
evo.status = 'wf-%s' % status_id |
|
561 |
status = 'wf-%s' % status_id |
|
562 | 562 |
if not self.evolution: |
563 | 563 |
self.evolution = [] |
564 |
elif (self.status == status |
|
565 |
and self.evolution[-1].status == status |
|
566 |
and not self.evolution[-1].comment |
|
567 |
and not self.evolution[-1].display_parts()): |
|
568 |
# if status do not change and last evolution is empty, |
|
569 |
# just update last jump time on last evolution, do not add one |
|
570 |
self.evolution[-1].last_jump_datetime = datetime.datetime.now() |
|
571 |
self.store() |
|
572 |
return |
|
573 |
evo = Evolution(self) |
|
574 |
evo.time = time.localtime() |
|
575 |
evo.status = status |
|
564 | 576 |
self.evolution.append(evo) |
565 |
self.status = evo.status
|
|
577 |
self.status = status |
|
566 | 578 |
self.store() |
567 | 579 | |
568 | 580 |
def get_url(self, backoffice = False): |
... | ... | |
855 | 867 |
def get_last_update_time(self): |
856 | 868 |
if hasattr(self, '_last_update_time'): |
857 | 869 |
return self._last_update_time |
858 |
if self.evolution and self.evolution[-1].time: |
|
870 |
if self.evolution and self.evolution[-1].last_jump_datetime: |
|
871 |
return self.evolution[-1].last_jump_datetime.timetuple() |
|
872 |
elif self.evolution and self.evolution[-1].time: |
|
859 | 873 |
return self.evolution[-1].time |
860 | 874 |
else: |
861 | 875 |
return self.receipt_time |
wcs/sql.py | ||
---|---|---|
375 | 375 |
who varchar, |
376 | 376 |
status varchar, |
377 | 377 |
time timestamp, |
378 |
last_jump_datetime timestamp, |
|
378 | 379 |
comment text, |
379 | 380 |
parts bytea, |
380 | 381 |
formdata_id integer REFERENCES %s (id) ON DELETE CASCADE)''' % ( |
... | ... | |
471 | 472 |
for field in (existing_fields - needed_fields): |
472 | 473 |
cur.execute('''ALTER TABLE %s DROP COLUMN %s CASCADE''' % (table_name, field)) |
473 | 474 | |
475 |
# migrations on _evolutions table |
|
476 |
cur.execute('''SELECT column_name FROM information_schema.columns |
|
477 |
WHERE table_schema = 'public' |
|
478 |
AND table_name = '%s_evolutions' |
|
479 |
''' % table_name) |
|
480 |
evo_existing_fields = set([x[0] for x in cur.fetchall()]) |
|
481 |
if 'last_jump_datetime' not in evo_existing_fields: |
|
482 |
cur.execute('''ALTER TABLE %s_evolutions ADD COLUMN last_jump_datetime timestamp''' % table_name) |
|
483 | ||
474 | 484 |
if rebuild_views or len(existing_fields - needed_fields): |
475 | 485 |
# views may have been dropped when dropping columns, so we recreate |
476 | 486 |
# them even if not asked to. |
... | ... | |
1201 | 1211 |
self._evolution = [] |
1202 | 1212 |
return self._evolution |
1203 | 1213 |
conn, cur = get_connection_and_cursor() |
1204 |
sql_statement = '''SELECT id, who, status, time, comment, parts FROM %s_evolutions |
|
1214 |
sql_statement = '''SELECT id, who, status, time, last_jump_datetime, |
|
1215 |
comment, parts FROM %s_evolutions |
|
1205 | 1216 |
WHERE formdata_id = %%(id)s |
1206 | 1217 |
ORDER BY id''' % self._table_name |
1207 | 1218 |
cur.execute(sql_statement, {'id': self.id}) |
... | ... | |
1218 | 1229 |
@classmethod |
1219 | 1230 |
def _row2evo(cls, row, formdata): |
1220 | 1231 |
o = wcs.formdata.Evolution(formdata) |
1221 |
o._sql_id, o.who, o.status, o.time, o.comment = [str_encode(x) for x in tuple(row[:5])] |
|
1232 |
o._sql_id, o.who, o.status, o.time, o.last_jump_datetime, o.comment = [ |
|
1233 |
str_encode(x) for x in tuple(row[:6])] |
|
1222 | 1234 |
if o.time: |
1223 | 1235 |
o.time = o.time.timetuple() |
1224 |
if row[5]:
|
|
1225 |
o.parts = cPickle.loads(str(row[5]))
|
|
1236 |
if row[6]:
|
|
1237 |
o.parts = cPickle.loads(str(row[6]))
|
|
1226 | 1238 |
return o |
1227 | 1239 | |
1228 | 1240 |
def set_evolution(self, value): |
... | ... | |
1243 | 1255 |
if not object_dict: |
1244 | 1256 |
return |
1245 | 1257 |
conn, cur = get_connection_and_cursor() |
1246 |
sql_statement = '''SELECT id, who, status, time, comment, parts, formdata_id |
|
1258 |
sql_statement = '''SELECT id, who, status, time, last_jump_datetime, |
|
1259 |
comment, parts, formdata_id |
|
1247 | 1260 |
FROM %s_evolutions''' % cls._table_name |
1248 | 1261 |
sql_statement += ''' WHERE formdata_id IN %(object_ids)s ORDER BY id''' |
1249 | 1262 |
cur.execute(sql_statement, {'object_ids': tuple(object_dict.keys())}) |
... | ... | |
1255 | 1268 |
row = cur.fetchone() |
1256 | 1269 |
if row is None: |
1257 | 1270 |
break |
1258 |
_sql_id, who, status, time, comment, parts, formdata_id = tuple(row[:7])
|
|
1271 |
_sql_id, who, status, time, last_jump_datetime, comment, parts, formdata_id = tuple(row[:8])
|
|
1259 | 1272 |
formdata = object_dict.get(formdata_id) |
1260 | 1273 |
if not formdata: |
1261 | 1274 |
continue |
... | ... | |
1354 | 1367 |
sql_dict.update({'id': evo._sql_id}) |
1355 | 1368 |
sql_statement = '''UPDATE %s_evolutions SET |
1356 | 1369 |
time = %%(time)s, |
1370 |
last_jump_datetime = %%(last_jump_datetime)s, |
|
1357 | 1371 |
status = %%(status)s, |
1358 | 1372 |
comment = %%(comment)s, |
1359 | 1373 |
parts = %%(parts)s |
... | ... | |
1362 | 1376 |
else: |
1363 | 1377 |
sql_statement = '''INSERT INTO %s_evolutions ( |
1364 | 1378 |
id, who, status, |
1365 |
time, comment, parts, |
|
1379 |
time, last_jump_datetime, |
|
1380 |
comment, parts, |
|
1366 | 1381 |
formdata_id) |
1367 | 1382 |
VALUES (DEFAULT, %%(who)s, %%(status)s, |
1368 |
%%(time)s, %%(comment)s, |
|
1383 |
%%(time)s, %%(last_jump_datetime)s, |
|
1384 |
%%(comment)s, |
|
1369 | 1385 |
%%(parts)s, %%(formdata_id)s) |
1370 | 1386 |
RETURNING id''' % self._table_name |
1371 | 1387 |
sql_dict.update({ |
1372 | 1388 |
'who': evo.who, |
1373 | 1389 |
'status': evo.status, |
1374 | 1390 |
'time': datetime.datetime.fromtimestamp(time.mktime(evo.time)), |
1391 |
'last_jump_datetime': evo.last_jump_datetime, |
|
1375 | 1392 |
'comment': evo.comment, |
1376 | 1393 |
'formdata_id': self.id, |
1377 | 1394 |
}) |
... | ... | |
2123 | 2140 |
return result |
2124 | 2141 | |
2125 | 2142 | |
2126 |
SQL_LEVEL = 26
|
|
2143 |
SQL_LEVEL = 27
|
|
2127 | 2144 | |
2128 | 2145 |
def migrate_global_views(conn, cur): |
2129 | 2146 |
cur.execute('''SELECT COUNT(*) FROM information_schema.tables |
... | ... | |
2201 | 2218 |
raise RuntimeError() |
2202 | 2219 |
if sql_level < 1: # 1: introduction of tracking_code table |
2203 | 2220 |
do_tracking_code_table() |
2204 |
if sql_level < 26:
|
|
2221 |
if sql_level < 27:
|
|
2205 | 2222 |
# 2: introduction of formdef_id in views |
2206 | 2223 |
# 5: add concerned_roles_array, is_at_endpoint and fts to views |
2207 | 2224 |
# 7: add backoffice_submission to tables |
... | ... | |
2216 | 2233 |
# 20: remove user hash stuff |
2217 | 2234 |
# 22: rebuild views |
2218 | 2235 |
# 26: add digest to formdata |
2236 |
# 27: add last_jump_datetime in evolutions tables |
|
2219 | 2237 |
migrate_views(conn, cur) |
2220 | 2238 |
if sql_level < 21: |
2221 | 2239 |
# 3: introduction of _structured for user fields |
wcs/templates/wcs/formdata_history.html | ||
---|---|---|
13 | 13 |
{% if evolution.status %} |
14 | 14 |
<div class="evolution-metadata"> |
15 | 15 |
<span class="status">{{evolution.get_status_label}}</span> |
16 |
<span class="time">{{evolution.datetime}}</span> |
|
16 |
<span class="time">{{evolution.datetime}} |
|
17 |
{% if evolution.last_jump_datetime and user.is_admin %} |
|
18 |
<span class="last-jump">({% trans "last check:" %} {{ evolution.last_jump_datetime }})</span> |
|
19 |
{% endif %} |
|
20 |
</span> |
|
17 | 21 |
</div> |
18 | 22 |
{% endif %} |
19 | 23 |
<div class="msg"> |
wcs/wf/jump.py | ||
---|---|---|
243 | 243 | |
244 | 244 |
if self.timeout: |
245 | 245 |
timeout = int(self.compute(self.timeout)) |
246 |
if formdata.evolution: |
|
247 |
last = formdata.evolution[-1].time |
|
248 |
else: |
|
249 |
last = formdata.receipt_time |
|
246 |
last = formdata.last_update_time |
|
250 | 247 |
if last: |
251 | 248 |
diff = time.time() - time.mktime(last) |
252 | 249 |
must_jump = (diff > timeout) and must_jump |
wcs/workflows.py | ||
---|---|---|
1040 | 1040 |
anchor_status = self.anchor_status_first or formdata.status |
1041 | 1041 |
for evolution in formdata.evolution: |
1042 | 1042 |
if evolution.status == anchor_status: |
1043 |
anchor_date = evolution.time |
|
1043 |
anchor_date = evolution.last_jump_datetime or evolution.time
|
|
1044 | 1044 |
break |
1045 | 1045 |
elif self.anchor == 'latest-arrival': |
1046 | 1046 |
anchor_status = self.anchor_status_latest or formdata.status |
1047 | 1047 |
for evolution in reversed(formdata.evolution): |
1048 | 1048 |
if evolution.status == anchor_status: |
1049 |
anchor_date = evolution.time |
|
1049 |
anchor_date = evolution.last_jump_datetime or evolution.time
|
|
1050 | 1050 |
break |
1051 | 1051 |
elif self.anchor == 'python': |
1052 | 1052 |
variables = get_publisher().substitutions.get_context_variables() |
1053 |
- |