Skip to content

Commit 7ee12a4

Browse files
committed
Clean up tests, add db upgrade test
1 parent 728322e commit 7ee12a4

File tree

5 files changed

+176
-14
lines changed

5 files changed

+176
-14
lines changed

api/dao/hierarchy.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,15 @@ def check_req(cont, req_k, req_v):
144144
"""
145145
Return True if container satisfies specific requirement.
146146
"""
147-
cont_v = cont.get(req_k)
147+
148+
# If looking at classification, translate to list rather than dictionary
149+
if req_k == 'classification':
150+
cont_v = []
151+
for v in cont.get('classification', {}).itervalues():
152+
cont_v.extend(v)
153+
else:
154+
cont_v = cont.get(req_k)
155+
148156
if cont_v:
149157
if isinstance(req_v, dict):
150158
for k,v in req_v.iteritems():

api/dao/liststorage.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import bson.objectid
33
import copy
44
import datetime
5-
import pymongo
65

76
from ..web.errors import APIStorageException, APIConflictException, APINotFoundException
87
from . import consistencychecker
@@ -117,12 +116,23 @@ def _get_el(self, _id, query_params):
117116
if result and result.get(self.list_name):
118117
return result.get(self.list_name)[0]
119118

119+
def _update_session_compliance(self, _id):
120+
if self.cont_name in ['sessions', 'acquisitions']:
121+
if self.cont_name == 'sessions':
122+
session_id = _id
123+
else:
124+
session_id = AcquisitionStorage().get_container(_id).get('session')
125+
SessionStorage().recalc_session_compliance(session_id)
126+
120127

121128
class FileStorage(ListStorage):
122129

123130
def __init__(self, cont_name):
124131
super(FileStorage,self).__init__(cont_name, 'files', use_object_id=True)
125132

133+
def _create_jobs(self, container_before):
134+
container_after = self.get_container(container_before['_id'])
135+
return rules.create_jobs(config.db, container_before, container_after, self.cont_name)
126136

127137
def _update_el(self, _id, query_params, payload, exclude_params):
128138
container_before = self.get_container(_id)
@@ -147,11 +157,9 @@ def _update_el(self, _id, query_params, payload, exclude_params):
147157
'$set': mod_elem
148158
}
149159

150-
container_after = self.dbc.find_one_and_update(query, update, return_document=pymongo.collection.ReturnDocument.AFTER)
151-
if not container_after:
152-
raise APINotFoundException('Could not find and modify {} {}. file not updated'.format(_id, self.cont_name))
153-
154-
jobs_spawned = rules.create_jobs(config.db, container_before, container_after, self.cont_name)
160+
self.dbc.find_one_and_update(query, update)
161+
self._update_session_compliance(_id)
162+
jobs_spawned = self._create_jobs(container_before)
155163

156164
return {
157165
'modified': 1,
@@ -164,12 +172,7 @@ def _delete_el(self, _id, query_params):
164172
if f['name'] == query_params['name']:
165173
f['deleted'] = datetime.datetime.utcnow()
166174
result = self.dbc.update_one({'_id': _id}, {'$set': {'files': files, 'modified': datetime.datetime.utcnow()}})
167-
if self.cont_name in ['sessions', 'acquisitions']:
168-
if self.cont_name == 'sessions':
169-
session_id = _id
170-
else:
171-
session_id = AcquisitionStorage().get_container(_id).get('session')
172-
SessionStorage().recalc_session_compliance(session_id)
175+
self._update_session_compliance(_id)
173176
return result
174177

175178
def _get_el(self, _id, query_params):
@@ -217,9 +220,13 @@ def modify_info(self, _id, query_params, payload):
217220
else:
218221
update['$set']['modified'] = datetime.datetime.utcnow()
219222

220-
return self.dbc.update_one(query, update)
223+
result = self.dbc.update_one(query, update)
224+
self._update_session_compliance(_id)
225+
return result
226+
221227

222228
def modify_classification(self, _id, query_params, payload):
229+
container_before = self.get_container(_id)
223230
update = {'$set': {'modified': datetime.datetime.utcnow()}}
224231

225232
if self.use_object_id:
@@ -265,6 +272,9 @@ def modify_classification(self, _id, query_params, payload):
265272

266273
self.dbc.update_one(query, d_update)
267274

275+
self._update_session_compliance(_id)
276+
self._create_jobs(container_before)
277+
268278

269279

270280
class StringListStorage(ListStorage):

bin/database.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,25 @@ def adjust_type(r):
14251425

14261426
config.db.project_rules.replace_one({'_id': rule['_id']}, rule)
14271427

1428+
return True
1429+
1430+
def upgrade_templates_to_43(project):
1431+
"""
1432+
Set any measurements keys to classification
1433+
"""
1434+
1435+
template = project['template']
1436+
1437+
for a in template.get('acquisitions', []):
1438+
for f in a.get('files', []):
1439+
if 'measurements' in f:
1440+
cl = f.pop('measurements')
1441+
f['classification'] = cl
1442+
1443+
config.db.projects.update_one({'_id': project['_id']}, {'$set': {'template': template}})
1444+
1445+
return True
1446+
14281447
def upgrade_to_43():
14291448
"""
14301449
Update classification for all files with existing measurements field
@@ -1443,6 +1462,9 @@ def upgrade_to_43():
14431462
]})
14441463
process_cursor(cursor, upgrade_rules_to_43)
14451464

1465+
cursor = config.db.projects.find({'template': {'$exists': True }})
1466+
process_cursor(cursor, upgrade_templates_to_43)
1467+
14461468

14471469

14481470

tests/integration_tests/python/test_containers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def test_project_template(data_builder, file_form, as_admin):
6363
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files', files=file_form('non-compliant.txt')).ok
6464
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files', files=file_form('compliant1.csv')).ok
6565
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files', files=file_form('compliant2.csv')).ok
66+
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files/compliant1.csv/classification', json={'add': {'custom': ['diffusion']}})
67+
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files/compliant2.csv/classification', json={'add': {'custom': ['diffusion']}})
6668

6769
# test the session before setting the template
6870
r = as_admin.get('/sessions/' + session)
@@ -79,6 +81,7 @@ def test_project_template(data_builder, file_form, as_admin):
7981
'files': [{
8082
'minimum': 2,
8183
'mimetype': 'text/csv',
84+
'classification': 'diffusion'
8285
}]
8386
}]
8487
})
@@ -95,6 +98,7 @@ def test_project_template(data_builder, file_form, as_admin):
9598
'files': [{
9699
'minimum': 2,
97100
'mimetype': 'text/csv',
101+
'classification': 'diffusion'
98102
}]
99103
}]
100104
})
@@ -152,6 +156,8 @@ def satisfies_template():
152156
assert as_admin.delete('/acquisitions/' + acquisition_2 + '/files/compliant2.csv').ok
153157
assert not satisfies_template()
154158
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files', files=file_form('compliant2.csv')).ok
159+
assert not satisfies_template()
160+
assert as_admin.post('/acquisitions/' + acquisition_2 + '/files/compliant2.csv/classification', json={'add': {'custom': ['diffusion']}})
155161

156162
# acquisitions.minimum
157163
assert satisfies_template()

tests/integration_tests/python/test_upgrades.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33

44
import bson
5+
import copy
56
import pytest
67

78

@@ -32,3 +33,118 @@ def test_42(data_builder, api_db, as_admin, database):
3233
# Verify archived was removed when false as well
3334
session_data = as_admin.get('/sessions/' + session2).json()
3435
assert 'archived' not in session_data
36+
37+
38+
def test_43(data_builder, randstr, api_db, as_admin, database, file_form):
39+
40+
# Set up files with measurements
41+
42+
assert True
43+
44+
containers = [
45+
('collections', data_builder.create_collection()),
46+
('projects', data_builder.create_project()),
47+
('sessions', data_builder.create_session()),
48+
('acquisitions', data_builder.create_acquisition())
49+
]
50+
51+
for c in containers:
52+
assert as_admin.post('/{}/{}/files'.format(c[0], c[1]), files=file_form('test.csv')).ok
53+
assert as_admin.post('/{}/{}/files'.format(c[0], c[1]), files=file_form('test2.csv')).ok
54+
api_db[c[0]].update_one({'_id': bson.ObjectId(c[1])},
55+
{'$set': { # Mangoes ...
56+
'files.0.measurements': ['diffusion', 'functional'],
57+
'files.1.measurements': ['diffusion', 'functional']
58+
}})
59+
60+
61+
# Set up rules referencing measurements
62+
63+
rule = {
64+
'all' : [
65+
{'type' : 'file.measurement', 'value' : 'diffusion'},
66+
{'type' : 'container.has-measurement', 'value' : 'tests', 'regex': True}
67+
],
68+
'any' : [
69+
{'type' : 'file.measurement', 'value' : 'diffusion'},
70+
{'type' : 'container.has-measurement', 'value' : 'tests', 'regex': True}
71+
],
72+
'name' : 'Run dcm2niix on dicom',
73+
'alg' : 'dcm2niix',
74+
'project_id' : 'site'
75+
}
76+
77+
api_db.project_rules.insert(copy.deepcopy(rule))
78+
api_db.project_rules.insert(copy.deepcopy(rule))
79+
80+
81+
# Set up session templates referencing measurements
82+
83+
t_project1 = data_builder.create_project()
84+
t_project2 = data_builder.create_project()
85+
86+
template = {
87+
'session': {'subject': {'code': '^compliant$'}},
88+
'acquisitions': [{
89+
'minimum': 1,
90+
'files': [{
91+
'minimum': 2,
92+
'measurements': 'diffusion'
93+
}]
94+
}]
95+
}
96+
97+
assert as_admin.post('/projects/' + t_project1 + '/template', json=template).ok
98+
assert as_admin.post('/projects/' + t_project2 + '/template', json=template).ok
99+
100+
101+
### RUN UPGRADE
102+
103+
database.upgrade_to_43()
104+
105+
####
106+
107+
108+
# Ensure files were updated
109+
for c in containers:
110+
files = as_admin.get('/{}/{}'.format(c[0], c[1])).json()['files']
111+
for f in files:
112+
assert f['classification'] == {'Custom': ['diffusion', 'functional']}
113+
114+
115+
# Ensure rules were updated
116+
rule_after = {
117+
'all' : [
118+
{'type' : 'file.classification', 'value' : 'diffusion'},
119+
{'type' : 'container.has-classification', 'value' : 'tests', 'regex': True}
120+
],
121+
'any' : [
122+
{'type' : 'file.classification', 'value' : 'diffusion'},
123+
{'type' : 'container.has-classification', 'value' : 'tests', 'regex': True}
124+
],
125+
'name' : 'Run dcm2niix on dicom',
126+
'alg' : 'dcm2niix'
127+
}
128+
129+
rules = as_admin.get('/site/rules').json()
130+
for r in rules:
131+
r.pop('_id')
132+
assert r == rule_after
133+
134+
135+
# Ensure templates were updated
136+
template_after = {
137+
'session': {'subject': {'code': '^compliant$'}},
138+
'acquisitions': [{
139+
'minimum': 1,
140+
'files': [{
141+
'minimum': 2,
142+
'classification': 'diffusion'
143+
}]
144+
}]
145+
}
146+
for p in [t_project1, t_project2]:
147+
assert as_admin.get('/projects/' + p).json()['template'] == template_after
148+
149+
150+

0 commit comments

Comments
 (0)