2020-02-28 16:14:30 +00:00
|
|
|
from typing import List, Optional, Union, Tuple, Dict
|
2020-02-26 23:06:51 +00:00
|
|
|
|
2019-12-18 19:02:17 +00:00
|
|
|
from connexion import NoContent
|
2020-02-26 23:06:51 +00:00
|
|
|
from flask import g
|
2019-12-18 19:02:17 +00:00
|
|
|
|
2020-02-26 23:06:51 +00:00
|
|
|
from crc import session, auth
|
2019-12-27 18:50:03 +00:00
|
|
|
from crc.api.common import ApiError, ApiErrorSchema
|
2020-02-07 16:34:44 +00:00
|
|
|
from crc.api.workflow import __get_workflow_api_model
|
2020-03-05 16:18:20 +00:00
|
|
|
from crc.models.api_models import WorkflowApiSchema
|
2020-02-27 16:17:58 +00:00
|
|
|
from crc.models.protocol_builder import ProtocolBuilderStatus, ProtocolBuilderStudy
|
2020-02-27 14:54:46 +00:00
|
|
|
from crc.models.study import StudyModelSchema, StudyModel
|
2020-03-05 16:18:20 +00:00
|
|
|
from crc.models.workflow import WorkflowModel, WorkflowSpecModel
|
2020-02-10 21:19:23 +00:00
|
|
|
from crc.services.workflow_processor import WorkflowProcessor
|
2020-02-27 16:17:58 +00:00
|
|
|
from crc.services.protocol_builder import ProtocolBuilderService
|
2019-12-18 19:02:17 +00:00
|
|
|
|
|
|
|
|
2020-02-26 23:06:51 +00:00
|
|
|
@auth.login_required
|
2019-12-18 19:02:17 +00:00
|
|
|
def all_studies():
|
2020-02-28 16:14:30 +00:00
|
|
|
return update_from_protocol_builder()
|
2019-12-18 19:02:17 +00:00
|
|
|
|
|
|
|
|
2020-02-27 15:30:16 +00:00
|
|
|
@auth.login_required
|
2020-01-03 16:44:24 +00:00
|
|
|
def add_study(body):
|
2020-01-14 16:45:12 +00:00
|
|
|
study = StudyModelSchema().load(body, session=session)
|
|
|
|
session.add(study)
|
|
|
|
session.commit()
|
2020-02-27 15:30:16 +00:00
|
|
|
|
2020-01-30 14:11:17 +00:00
|
|
|
# FIXME: We need to ask the protocol builder what workflows to add to the study, not just add them all.
|
|
|
|
for spec in session.query(WorkflowSpecModel).all():
|
2020-02-10 21:19:23 +00:00
|
|
|
WorkflowProcessor.create(study.id, spec.id)
|
2020-01-03 16:44:24 +00:00
|
|
|
return StudyModelSchema().dump(study)
|
|
|
|
|
|
|
|
|
2020-02-27 15:30:16 +00:00
|
|
|
@auth.login_required
|
2020-01-03 16:44:24 +00:00
|
|
|
def update_study(study_id, body):
|
|
|
|
if study_id is None:
|
|
|
|
error = ApiError('unknown_study', 'Please provide a valid Study ID.')
|
|
|
|
return ApiErrorSchema.dump(error), 404
|
|
|
|
|
2020-01-14 16:45:12 +00:00
|
|
|
study = session.query(StudyModel).filter_by(id=study_id).first()
|
2020-01-03 16:44:24 +00:00
|
|
|
|
|
|
|
if study is None:
|
|
|
|
error = ApiError('unknown_study', 'The study "' + study_id + '" is not recognized.')
|
|
|
|
return ApiErrorSchema.dump(error), 404
|
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
schema = StudyModelSchema()
|
|
|
|
study = schema.load(body, session=session, instance=study, partial=True)
|
2020-01-14 16:45:12 +00:00
|
|
|
session.add(study)
|
|
|
|
session.commit()
|
2020-02-28 16:14:30 +00:00
|
|
|
return schema.dump(study)
|
2020-01-03 16:44:24 +00:00
|
|
|
|
|
|
|
|
2020-02-27 15:30:16 +00:00
|
|
|
@auth.login_required
|
2019-12-18 19:02:17 +00:00
|
|
|
def get_study(study_id):
|
2020-01-14 16:45:12 +00:00
|
|
|
study = session.query(StudyModel).filter_by(id=study_id).first()
|
2020-01-03 16:44:24 +00:00
|
|
|
schema = StudyModelSchema()
|
2019-12-18 19:02:17 +00:00
|
|
|
if study is None:
|
|
|
|
return NoContent, 404
|
|
|
|
return schema.dump(study)
|
|
|
|
|
2020-02-26 23:06:51 +00:00
|
|
|
|
2020-02-27 15:30:16 +00:00
|
|
|
@auth.login_required
|
2020-02-20 18:30:04 +00:00
|
|
|
def update_from_protocol_builder():
|
2020-02-27 15:30:16 +00:00
|
|
|
"""Updates the list of known studies for a given user based on data received from
|
|
|
|
the protocol builder."""
|
|
|
|
|
|
|
|
user = g.user
|
|
|
|
""":type: crc.models.user.UserModel"""
|
|
|
|
|
|
|
|
# Get studies matching this user from Protocol Builder
|
2020-02-28 16:14:30 +00:00
|
|
|
pb_studies: List[ProtocolBuilderStudy] = get_user_pb_studies()
|
2020-02-27 15:30:16 +00:00
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
# Get studies from the database
|
2020-02-27 15:30:16 +00:00
|
|
|
db_studies = session.query(StudyModel).filter_by(user_uid=user.uid).all()
|
|
|
|
db_study_ids = list(map(lambda s: s.id, db_studies))
|
2020-02-28 16:14:30 +00:00
|
|
|
pb_study_ids = list(map(lambda s: s['STUDYID'], pb_studies))
|
2020-02-27 15:30:16 +00:00
|
|
|
|
|
|
|
for pb_study in pb_studies:
|
2020-02-28 16:14:30 +00:00
|
|
|
|
|
|
|
# Update studies with latest data from Protocol Builder
|
|
|
|
if pb_study['STUDYID'] in db_study_ids:
|
|
|
|
update_study(pb_study['STUDYID'], map_pb_study_to_study(pb_study))
|
|
|
|
|
|
|
|
# Add studies from Protocol Builder that aren't in the database yet
|
|
|
|
else:
|
|
|
|
new_study = map_pb_study_to_study(pb_study)
|
|
|
|
add_study(new_study)
|
2019-12-18 19:02:17 +00:00
|
|
|
|
2020-02-27 16:17:58 +00:00
|
|
|
# Mark studies as inactive that are no longer in Protocol Builder
|
|
|
|
for study_id in db_study_ids:
|
|
|
|
if study_id not in pb_study_ids:
|
2020-03-03 20:46:20 +00:00
|
|
|
update_study(
|
|
|
|
study_id=study_id,
|
|
|
|
body={
|
|
|
|
'inactive': True,
|
2020-03-04 01:56:46 +00:00
|
|
|
'protocol_builder_status': ProtocolBuilderStatus.INACTIVE.name
|
2020-03-03 20:46:20 +00:00
|
|
|
}
|
|
|
|
)
|
2020-02-27 16:17:58 +00:00
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
# Return updated studies
|
|
|
|
updated_studies = session.query(StudyModel).filter_by(user_uid=user.uid).all()
|
|
|
|
results = StudyModelSchema(many=True).dump(updated_studies)
|
|
|
|
return results
|
2020-02-26 23:06:51 +00:00
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
|
|
|
|
@auth.login_required
|
2019-12-18 19:02:17 +00:00
|
|
|
def post_update_study_from_protocol_builder(study_id):
|
2020-02-27 15:30:16 +00:00
|
|
|
"""Update a single study based on data received from
|
2020-02-20 18:30:04 +00:00
|
|
|
the protocol builder."""
|
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
pb_studies: List[ProtocolBuilderStudy] = get_user_pb_studies()
|
|
|
|
for pb_study in pb_studies:
|
|
|
|
if pb_study['STUDYID'] == study_id:
|
|
|
|
return update_study(study_id, map_pb_study_to_study(pb_study))
|
|
|
|
|
2019-12-18 19:02:17 +00:00
|
|
|
return NoContent, 304
|
|
|
|
|
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
@auth.login_required
|
2019-12-18 19:02:17 +00:00
|
|
|
def get_study_workflows(study_id):
|
2020-02-07 16:34:44 +00:00
|
|
|
workflow_models = session.query(WorkflowModel).filter_by(study_id=study_id).all()
|
|
|
|
api_models = []
|
|
|
|
for workflow_model in workflow_models:
|
|
|
|
processor = WorkflowProcessor(workflow_model.workflow_spec_id,
|
|
|
|
workflow_model.bpmn_workflow_json)
|
2020-02-26 23:06:51 +00:00
|
|
|
api_models.append(__get_workflow_api_model(processor))
|
2020-02-07 16:34:44 +00:00
|
|
|
schema = WorkflowApiSchema(many=True)
|
|
|
|
return schema.dump(api_models)
|
2019-12-18 19:02:17 +00:00
|
|
|
|
|
|
|
|
2020-02-28 16:14:30 +00:00
|
|
|
@auth.login_required
|
2019-12-18 19:02:17 +00:00
|
|
|
def add_workflow_to_study(study_id, body):
|
2020-01-14 16:45:12 +00:00
|
|
|
workflow_spec_model = session.query(WorkflowSpecModel).filter_by(id=body["id"]).first()
|
2019-12-18 19:02:17 +00:00
|
|
|
if workflow_spec_model is None:
|
|
|
|
error = ApiError('unknown_spec', 'The specification "' + body['id'] + '" is not recognized.')
|
|
|
|
return ApiErrorSchema.dump(error), 404
|
2020-02-10 21:19:23 +00:00
|
|
|
processor = WorkflowProcessor.create(study_id, workflow_spec_model.id)
|
|
|
|
return WorkflowApiSchema().dump(__get_workflow_api_model(processor))
|
2020-02-28 16:14:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
@auth.login_required
|
|
|
|
def get_user_pb_studies() -> List[ProtocolBuilderStudy]:
|
|
|
|
"""Get studies from Protocol Builder matching the given user"""
|
|
|
|
|
|
|
|
user = g.user
|
|
|
|
""":type: crc.models.user.UserModel"""
|
|
|
|
|
|
|
|
return ProtocolBuilderService.get_studies(user.uid)
|
|
|
|
|
|
|
|
|
|
|
|
def map_pb_study_to_study(pb_study):
|
|
|
|
"""Translates the given dict of ProtocolBuilderStudy properties to dict of StudyModel attributes"""
|
|
|
|
prop_map = {
|
|
|
|
'STUDYID': 'id',
|
|
|
|
'HSRNUMBER': 'hsr_number',
|
|
|
|
'TITLE': 'title',
|
|
|
|
'NETBADGEID': 'user_uid',
|
|
|
|
'DATE_MODIFIED': 'last_updated',
|
|
|
|
}
|
|
|
|
study_info = {}
|
|
|
|
|
|
|
|
# Translate Protocol Builder property names to Study attributes
|
|
|
|
for k, v in pb_study.items():
|
|
|
|
if k in prop_map:
|
|
|
|
study_info[prop_map[k]] = v
|
|
|
|
|
2020-03-03 20:46:20 +00:00
|
|
|
# Translate Protocol Builder states to enum values
|
|
|
|
status = ProtocolBuilderStatus.DRAFT
|
2020-03-04 01:56:46 +00:00
|
|
|
pb_details = ProtocolBuilderService.get_study_details(pb_study['STUDYID'])
|
2020-02-28 16:14:30 +00:00
|
|
|
|
2020-03-04 01:56:46 +00:00
|
|
|
if 'Q_COMPLETE' in pb_study and pb_study['Q_COMPLETE']:
|
|
|
|
if 'UPLOAD_COMPLETE' in pb_details and pb_details['UPLOAD_COMPLETE']:
|
2020-03-03 21:01:36 +00:00
|
|
|
if 'HSRNUMBER' in pb_study and pb_study['HSRNUMBER']:
|
2020-03-03 20:46:20 +00:00
|
|
|
status = ProtocolBuilderStatus.REVIEW_COMPLETE
|
|
|
|
else:
|
|
|
|
status = ProtocolBuilderStatus.IN_REVIEW
|
|
|
|
else:
|
|
|
|
status = ProtocolBuilderStatus.IN_PROCESS
|
2020-02-28 16:14:30 +00:00
|
|
|
|
2020-03-04 01:56:46 +00:00
|
|
|
study_info['protocol_builder_status'] = status.name
|
2020-03-03 20:46:20 +00:00
|
|
|
study_info['inactive'] = False
|
2020-02-28 16:14:30 +00:00
|
|
|
return study_info
|
|
|
|
|