From 06dedd8a32393e36e191efcb520501094e79c4a7 Mon Sep 17 00:00:00 2001 From: Kelly McDonald Date: Tue, 2 Mar 2021 10:03:53 -0500 Subject: [PATCH] Add short_title to study, change update_study task to use kw arguments and add the short_title to the update_study script --- crc/models/study.py | 7 ++-- crc/scripts/study_info.py | 1 + crc/scripts/update_study.py | 35 ++++++++----------- crc/services/study_service.py | 9 ++--- migrations/versions/f28ee3722c49_.py | 28 +++++++++++++++ tests/data/message_event/message_event.bpmn | 4 +-- .../study_cancellations.bpmn | 6 ++-- tests/study/test_update_study_script.py | 20 ++++++++--- tests/test_message_event.py | 2 +- 9 files changed, 74 insertions(+), 38 deletions(-) create mode 100644 migrations/versions/f28ee3722c49_.py diff --git a/crc/models/study.py b/crc/models/study.py index 58ab895a..d6209013 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -40,6 +40,7 @@ class StudyModel(db.Model): __tablename__ = 'study' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String) + short_title = db.Column(db.String, nullable=True) last_updated = db.Column(db.DateTime(timezone=True), default=func.now()) status = db.Column(db.Enum(StudyStatus)) irb_status = db.Column(db.Enum(IrbStatus)) @@ -161,7 +162,7 @@ class CategorySchema(ma.Schema): class Study(object): - def __init__(self, title, last_updated, primary_investigator_id, user_uid, + def __init__(self, title, short_title, last_updated, primary_investigator_id, user_uid, id=None, status=None, irb_status=None, comment="", sponsor="", hsr_number="", ind_number="", categories=[], files=[], approvals=[], enrollment_date=None, events_history=[], @@ -172,6 +173,7 @@ class Study(object): self.last_activity_date = last_activity_date self.last_activity_user = last_activity_user self.title = title + self.short_title = short_title self.last_updated = last_updated self.status = status self.irb_status = irb_status @@ -243,6 +245,7 @@ class StudySchema(ma.Schema): protocol_builder_status = EnumField(StudyStatus, by_value=True) status = EnumField(StudyStatus, by_value=True) hsr_number = fields.String(allow_none=True) + short_title = fields.String(allow_none=True) sponsor = fields.String(allow_none=True) ind_number = fields.String(allow_none=True) files = fields.List(fields.Nested(FileSchema), dump_only=True) @@ -251,7 +254,7 @@ class StudySchema(ma.Schema): class Meta: model = Study - additional = ["id", "title", "last_updated", "primary_investigator_id", "user_uid", + additional = ["id", "title", "short_title", "last_updated", "primary_investigator_id", "user_uid", "sponsor", "ind_number", "files", "enrollment_date", "create_user_display", "last_activity_date","last_activity_user", "events_history"] diff --git a/crc/scripts/study_info.py b/crc/scripts/study_info.py index a5d579be..90bbd866 100644 --- a/crc/scripts/study_info.py +++ b/crc/scripts/study_info.py @@ -186,6 +186,7 @@ Returns information specific to the protocol. "info": { "id": 12, "title": "test", + "short_title": "tst", "primary_investigator_id":21, "user_uid": "dif84", "sponsor": "sponsor", diff --git a/crc/scripts/update_study.py b/crc/scripts/update_study.py index ffb9f68e..340665e0 100644 --- a/crc/scripts/update_study.py +++ b/crc/scripts/update_study.py @@ -15,44 +15,37 @@ class mock_study: class UpdateStudy(Script): argument_error_message = "You must supply at least one argument to the " \ - "update_study task, in the form [study_field]:[value]", + "update_study task, in the form [study_field]=[value]", def get_description(self): return """ Allows you to set specific attributes on the Study model by mapping them to -values in the task data. Should be called with the value to set (either title, or pi) -followed by a ":" and then the value to use in dot notation. +values in the task data. Should be called with the value to set (either title, short_title, or pi) Example: -UpdateStudy title:PIComputingID.label pi:PIComputingID.value +update_study(title=PIComputingID.label, short_title="Really Short Name") """ def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): study = mock_study - self.__update_study(task, study, *args) + self.__update_study(task, study, *args, **kwargs) def do_task(self, task, study_id, workflow_id, *args, **kwargs): study = db.session.query(StudyModel).filter(StudyModel.id == study_id).first() - self.__update_study(task, study, *args) + self.__update_study(task, study, *args, **kwargs) db.session.add(study) - def __update_study(self, task, study, *args): - if len(args) < 1: + def __update_study(self, task, study, *args, **kwargs): + if len(kwargs.keys()) < 1: raise ApiError.from_task("missing_argument", self.argument_error_message, task=task) - for arg in args: - try: - field, value_lookup = arg.split(':') - except: - raise ApiError.from_task("invalid_argument", self.argument_error_message, - task=task) - - value = task.workflow.script_engine.evaluate_expression(task, value_lookup) - - if field.lower() == "title": - study.title = value - elif field.lower() == "pi": - study.primary_investigator_id = value + for arg in kwargs.keys(): + if arg.lower() == "title": + study.title = kwargs[arg] + elif arg.lower() == "short_title": + study.short_title = kwargs[arg] + elif arg.lower() == "pi": + study.primary_investigator_id = kwargs[arg] else: raise ApiError.from_task("invalid_argument", self.argument_error_message, task=task) diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 69c24a34..48c326a6 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -36,7 +36,7 @@ class StudyService(object): studies = [] for study_model in db_studies: - studies.append(StudyService.get_study(study_model.id, study_model)) + studies.append(StudyService.get_study(study_model.id, study_model,do_status=False)) return studies @staticmethod @@ -51,7 +51,7 @@ class StudyService(object): return studies @staticmethod - def get_study(study_id, study_model: StudyModel = None): + def get_study(study_id, study_model: StudyModel = None, do_status=True): """Returns a study model that contains all the workflows organized by category. IMPORTANT: This is intended to be a lightweight call, it should never involve loading up and executing all the workflows in a study to calculate information.""" @@ -81,8 +81,9 @@ class StudyService(object): if study.status != StudyStatus.abandoned: # this line is taking 99% of the time that is used in get_study. # see ticket #196 - status = StudyService.__get_study_status(study_model) - study.warnings = StudyService.__update_status_of_workflow_meta(workflow_metas, status) + if do_status: + status = StudyService.__get_study_status(study_model) + study.warnings = StudyService.__update_status_of_workflow_meta(workflow_metas, status) # Group the workflows into their categories. for category in study.categories: diff --git a/migrations/versions/f28ee3722c49_.py b/migrations/versions/f28ee3722c49_.py new file mode 100644 index 00000000..19503dab --- /dev/null +++ b/migrations/versions/f28ee3722c49_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: f28ee3722c49 +Revises: cb892916166a +Create Date: 2021-03-02 08:30:22.879266 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f28ee3722c49' +down_revision = 'cb892916166a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('study', sa.Column('short_title', sa.String(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('study', 'short_title') + # ### end Alembic commands ### diff --git a/tests/data/message_event/message_event.bpmn b/tests/data/message_event/message_event.bpmn index 4b4b8857..079c5b53 100644 --- a/tests/data/message_event/message_event.bpmn +++ b/tests/data/message_event/message_event.bpmn @@ -1,5 +1,5 @@ - + Flow_0xym55y @@ -7,7 +7,7 @@ Flow_16q1uec - update_study("title:'New Title'") + update_study(title='New Title') print('New Title') diff --git a/tests/data/study_cancellations/study_cancellations.bpmn b/tests/data/study_cancellations/study_cancellations.bpmn index b164ce19..a73c30c9 100644 --- a/tests/data/study_cancellations/study_cancellations.bpmn +++ b/tests/data/study_cancellations/study_cancellations.bpmn @@ -1,5 +1,5 @@ - + Flow_0xym55y @@ -7,7 +7,7 @@ Flow_16q1uec - update_study("title:'New Title'") + update_study(title='New Title') print('New Title') @@ -58,7 +58,7 @@ print('New Title') <H1>Cancel Message</H1> Flow_13xidv2 - update_study("title:'Second Title'") + update_study(title='Second Title') print('Second Title') diff --git a/tests/study/test_update_study_script.py b/tests/study/test_update_study_script.py index df59ffc2..6e46fdee 100644 --- a/tests/study/test_update_study_script.py +++ b/tests/study/test_update_study_script.py @@ -2,7 +2,7 @@ from tests.base_test import BaseTest from crc.scripts.update_study import UpdateStudy from crc.services.workflow_processor import WorkflowProcessor - +from box import Box class TestUpdateStudyScript(BaseTest): @@ -12,12 +12,22 @@ class TestUpdateStudyScript(BaseTest): workflow = self.create_workflow('empty_workflow') processor = WorkflowProcessor(workflow) task = processor.next_task() - task.data = {"details": { + details = Box({ "label": "My New Title", - "value": "dhf8r"} - } + "short": "My New Short Title", + "value": "dhf8r"}) + script = UpdateStudy() - script.do_task(task, workflow.study_id, workflow.id, "title:details.label", "pi:details.value") + # note that we changed where the argument gets evaluated + # previsously, it took the arguments and then evaluated them within the script + # now, it evaluates the arugments in the context of the main script so they get + # evaluated before they are passed to the script - + # this allows us to do a lot more things like strings, functions, etc. + # and it makes the arguments less confusing to use. + script.do_task(task, workflow.study_id, workflow.id, title = details.label, + short_title = details.short, + pi = details.value) self.assertEqual("My New Title", workflow.study.title) + self.assertEqual("My New Short Title", workflow.study.short_title) self.assertEqual("dhf8r", workflow.study.primary_investigator_id) diff --git a/tests/test_message_event.py b/tests/test_message_event.py index 60ab0ac9..f761871b 100644 --- a/tests/test_message_event.py +++ b/tests/test_message_event.py @@ -26,7 +26,7 @@ class TestMessageEvent(BaseTest): headers=self.logged_in_headers(), content_type="application/json") - # set_current_task should call the interupt (signal) task + # set_current_task should call the interrupt (signal) task # which should run the script in our task # # test to see if our changes made it to the DB