From 1ba13a9f8601e05826ddbd255c712202e4ec9e8e Mon Sep 17 00:00:00 2001 From: Megan Henning <meganhenning@flywheel.io> Date: Wed, 28 Sep 2016 12:05:37 -0500 Subject: [PATCH] Add endpoint for session compliance calc --- api/api.py | 3 +- api/dao/containerstorage.py | 31 +++++++ api/handlers/containerhandler.py | 11 ++- raml/examples/input/project-template.json | 99 +++++++++-------------- raml/resources/projects.raml | 40 +++++++++ 5 files changed, 118 insertions(+), 66 deletions(-) create mode 100644 raml/resources/projects.raml diff --git a/api/api.py b/api/api.py index 1c53a028..c51c2708 100644 --- a/api/api.py +++ b/api/api.py @@ -172,7 +172,8 @@ routes = [ webapp2.Route(_format(r'/api/users/<uid:{user_id_re}>/<cont_name:{cont_name_re}>'), containerhandler.ContainerHandler, name='user_conts', handler_method='get_all_for_user', methods=['GET']), webapp2.Route(r'/api/projects/groups', containerhandler.ContainerHandler, handler_method='get_groups_with_project', methods=['GET']), - webapp2.Route(_format(r'/api/projects/<cid:{cid_re}>/template'), containerhandler.ContainerHandler, handler_method='set_project_template', methods=['POST']), + webapp2.Route(_format(r'/api/projects/<cid:{cid_re}>/template'), containerhandler.ContainerHandler, handler_method='set_project_template', methods=['POST']), + webapp2.Route(_format(r'/api/projects/<cid:{cid_re}>/template/recalc'), containerhandler.ContainerHandler, handler_method='calculate_project_compliance', methods=['POST']), webapp2.Route(_format(r'/api/<par_cont_name:groups>/<par_id:{group_id_re}>/<cont_name:projects>'), containerhandler.ContainerHandler, name='cont_sublist_groups', handler_method='get_all', methods=['GET']), webapp2.Route(_format(r'/api/<par_cont_name:{cont_name_re}>/<par_id:{cid_re}>/<cont_name:{cont_name_re}>'), containerhandler.ContainerHandler, name='cont_sublist', handler_method='get_all', methods=['GET']), diff --git a/api/dao/containerstorage.py b/api/dao/containerstorage.py index 6b4c4eac..cf1218ca 100644 --- a/api/dao/containerstorage.py +++ b/api/dao/containerstorage.py @@ -39,6 +39,8 @@ class ContainerStorage(object): """ if cont_name == 'groups': return GroupStorage() + elif cont_name == 'projects': + return ProjectStorage() elif cont_name == 'sessions': return SessionStorage() elif cont_name == 'acquisitions': @@ -158,6 +160,28 @@ class GroupStorage(ContainerStorage): }, upsert=True) + +class ProjectStorage(ContainerStorage): + + def __init__(self): + super(ProjectStorage,self).__init__('projects', use_object_id=True) + + def recalc_sessions_compliance(self, project_id): + project = self.get_container(project_id, get_children=True) + if not project: + raise APINotFoundException('Could not find project {}'.format(project_id)) + template = json.loads(project.get('template',{})) + if not template: + return + else: + changed_sessions = [] + session_storage = SessionStorage() + for s in project.get('sessions', []): + changed = session_storage.recalc_session_compliance(s['_id'], session=s, template=template) + if changed: + changed_sessions.append(s['_id']) + + class SessionStorage(ContainerStorage): def __init__(self): @@ -181,6 +205,10 @@ class SessionStorage(ContainerStorage): return super(SessionStorage, self).update_el(_id, payload, recursive, r_payload, replace_metadata) def recalc_session_compliance(self, session_id, session=None, template=None): + """ + Calculates a session's compliance with the project's session template. + Returns True if the status changed, False otherwise + """ if session is None: session = self.get_container(session_id) if session is None: @@ -192,6 +220,9 @@ class SessionStorage(ContainerStorage): if session.get('satisfies_template') != satisfies_template: update = {'satisfies_template': satisfies_template} super(SessionStorage, self).update_el(session_id, update) + return True + return False + class AcquisitionStorage(ContainerStorage): diff --git a/api/handlers/containerhandler.py b/api/handlers/containerhandler.py index d1c02b77..ee44a4b6 100644 --- a/api/handlers/containerhandler.py +++ b/api/handlers/containerhandler.py @@ -50,7 +50,7 @@ class ContainerHandler(base.RequestHandler): # "use_object_id" implies that the container ids are converted to ObjectId container_handler_configurations = { 'projects': { - 'storage': containerstorage.ContainerStorage('projects', use_object_id=use_object_id['projects']), + 'storage': containerstorage.ProjectStorage(), 'permchecker': containerauth.default_container, 'parent_storage': containerstorage.GroupStorage(), 'storage_schema_file': 'project.json', @@ -62,7 +62,7 @@ class ContainerHandler(base.RequestHandler): 'sessions': { 'storage': containerstorage.SessionStorage(), 'permchecker': containerauth.default_container, - 'parent_storage': containerstorage.ContainerStorage('projects', use_object_id=use_object_id['projects']), + 'parent_storage': containerstorage.ProjectStorage(), 'storage_schema_file': 'session.json', 'payload_schema_file': 'session.json', 'list_projection': {'metadata': 0}, @@ -485,7 +485,6 @@ class ContainerHandler(base.RequestHandler): def set_project_template(self, **kwargs): project_id = kwargs.pop('cid') - log.debug('the project_id is {}'.format(project_id)) self.config = self.container_handler_configurations['projects'] self.storage = self.config['storage'] container = self._get_container(project_id) @@ -509,6 +508,12 @@ class ContainerHandler(base.RequestHandler): else: self.abort(404, 'Could not find project {}'.format(project_id)) + def calculate_project_compliance(self, **kwargs): + project_id = kwargs.pop('cid') + self.config = self.container_handler_configurations['projects'] + self.storage = self.config['storage'] + return {'sessions_changed': self.storage.recalc_sesssions_compliance(project_id)} + def _get_validators(self): mongo_schema_uri = validators.schema_uri('mongo', self.config.get('storage_schema_file')) mongo_validator = validators.decorator_from_schema_path(mongo_schema_uri) diff --git a/raml/examples/input/project-template.json b/raml/examples/input/project-template.json index 9d0f93d7..6e5b25d4 100644 --- a/raml/examples/input/project-template.json +++ b/raml/examples/input/project-template.json @@ -5,76 +5,51 @@ "properties": { "label": { "type": "string", - "pattern": "^(?i)functional$" } + "pattern": "^(?i)test_pattern$" } } } }, "acquisitions": [ - { - "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "measurement": { - "type": "string", - "pattern": "^[aA]natomical$" } - }, - "required": ["measurement"] - }, - "minimum": 2, - "files": [ - { - "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - - "type": { "enum": ["nifti"] } - }, - "required": ["type"] + { + "schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "measurement": { + "type": "string", + "pattern": "^[aA]natomical$" } }, - "minimum": 2 + "required": ["measurement"] }, - { - "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "type": { "enum": ["dicom"] } - }, - "required": ["type"] + "minimum": 2 + }, + { + "schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "measurement": { + "type": "string", + "pattern": "^(?i)functional$" } }, - "minimum": 2 - } - ] - }, - { - "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "measurement": { - "type": "string", - "pattern": "^(?i)functional$" } + "required": ["measurement"] }, - "required": ["measurement"] + "minimum": 1 }, - "minimum": 3 - } - { - "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "measurement": { "enum": ["Localizer"] }, - "label": { - "type": "string", - "pattern": "t1" - } + { + "schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "measurement": { "enum": ["Localizer"] }, + "label": { + "type": "string", + "pattern": "t1" + } + }, + "required": ["label", "measurement"] }, - "required": ["label", "measurement"] - }, - "minimum": 2 - } - ] + "minimum": 1 + } + ] } diff --git a/raml/resources/projects.raml b/raml/resources/projects.raml new file mode 100644 index 00000000..58864787 --- /dev/null +++ b/raml/resources/projects.raml @@ -0,0 +1,40 @@ +/{ProjectId}/template: + uriParameters: + ProjectId: + type: string + required: true + post: + description: Set the session template for a project + body: + application/json: + example: !include ../examples/input/project-template.json + schema: !include ../schemas/input/project-template.json + responses: + 200: + description: Template was saved + body: + application/json: + example: | + {"modified": 1} + 404: + description: Project was not found + +/{ProjectId}/template/recalc: + uriParameters: + ProjectId: + type: string + required: true + post: + description: | + Recalculate if sessions in the project satisfy the template. + Returns list of modified sessions + body: + application/json: + example: !include ../examples/input/project-template.json + schema: !include ../schemas/input/project-template.json + responses: + 200: + description: Project's sessions' compliance was recalculated, returns list of + 404: + description: Project was not found + -- GitLab