diff --git a/crc/api.yml b/crc/api.yml index 09cdc5a3..09b70e4d 100644 --- a/crc/api.yml +++ b/crc/api.yml @@ -746,7 +746,7 @@ components: example: "2019-12-25T09:12:33.001Z" protocol_builder_status: type: string - enum: [out_of_date, in_process, complete, updating] + enum: [DRAFT, IN_PROCESS, IN_REVIEW, REVIEW_COMPLETE, INACTIVE] example: done user_uid: type: string diff --git a/crc/api/study.py b/crc/api/study.py index 159f8815..3bbd085e 100644 --- a/crc/api/study.py +++ b/crc/api/study.py @@ -89,7 +89,13 @@ def update_from_protocol_builder(): # 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: - update_study(study_id=study_id, body={'inactive': True}) + update_study( + study_id=study_id, + body={ + 'inactive': True, + 'protocol_builder_status': ProtocolBuilderStatus.INACTIVE.name + } + ) # Return updated studies updated_studies = session.query(StudyModel).filter_by(user_uid=user.uid).all() @@ -158,10 +164,20 @@ def map_pb_study_to_study(pb_study): if k in prop_map: study_info[prop_map[k]] = v - if pb_study['Q_COMPLETE']: - study_info['protocol_builder_status'] = ProtocolBuilderStatus.complete._value_ - else: - study_info['protocol_builder_status'] = ProtocolBuilderStatus.in_process._value_ + # Translate Protocol Builder states to enum values + status = ProtocolBuilderStatus.DRAFT + pb_details = ProtocolBuilderService.get_study_details(pb_study['STUDYID']) + if 'Q_COMPLETE' in pb_study and pb_study['Q_COMPLETE']: + if 'UPLOAD_COMPLETE' in pb_details and pb_details['UPLOAD_COMPLETE']: + if 'HSRNUMBER' in pb_study and pb_study['HSRNUMBER']: + status = ProtocolBuilderStatus.REVIEW_COMPLETE + else: + status = ProtocolBuilderStatus.IN_REVIEW + else: + status = ProtocolBuilderStatus.IN_PROCESS + + study_info['protocol_builder_status'] = status.name + study_info['inactive'] = False return study_info diff --git a/crc/models/protocol_builder.py b/crc/models/protocol_builder.py index 436e0027..a52a1ea0 100644 --- a/crc/models/protocol_builder.py +++ b/crc/models/protocol_builder.py @@ -18,10 +18,11 @@ class ProtocolBuilderInvestigatorType(enum.Enum): class ProtocolBuilderStatus(enum.Enum): - out_of_date = "out_of_date" - in_process = "in_process" - complete = "complete" - updating = "updating" + DRAFT = 'draft', # !Q_COMPLETE + IN_PROCESS = 'in_process', # Q_COMPLETE && !UPLOAD_COMPLETE && !HSRNUMBER + IN_REVIEW = 'in_review', # Q_COMPLETE && (!UPLOAD_COMPLETE || !HSRNUMBER) + REVIEW_COMPLETE = 'review_complete', # Q_COMPLETE && UPLOAD_COMPLETE && HSRNUMBER + INACTIVE = 'inactive', # Not found in PB class ProtocolBuilderStudy(object): diff --git a/crc/static/bpmn/crc2_training_session_data_security_plan/crc2_training_session_data_security_plan.bpmn b/crc/static/bpmn/crc2_training_session_data_security_plan/crc2_training_session_data_security_plan.bpmn index f11e9a4e..453ba1ad 100644 --- a/crc/static/bpmn/crc2_training_session_data_security_plan/crc2_training_session_data_security_plan.bpmn +++ b/crc/static/bpmn/crc2_training_session_data_security_plan/crc2_training_session_data_security_plan.bpmn @@ -98,7 +98,7 @@ - + @@ -484,78 +484,49 @@ Indicate all the possible formats in which you will transmit your data outside o SequenceFlow_08rwbhm SequenceFlow_0uewki3 SequenceFlow_0lere0k - SequenceFlow_14p4mbl + Flow_1h5ufzp - Transmission Method of data that will be mailed or faxed (FedEx, UPS, certified mail, etc.) Check all that apply: - + - - + + - - + + - + - - + + - + - - + + SequenceFlow_1mnmo6p SequenceFlow_0lere0k - - Data Security Plan Study Team Notes - - - - - - - - - - - - - - - - - - - - SequenceFlow_14p4mbl - SequenceFlow_18ik6tv - SequenceFlow_02pq2w4 - - Flow_0bvraie - SequenceFlow_0mgwas4 SequenceFlow_01hl869 - SequenceFlow_18ik6tv DateTransmittedOutside == True @@ -566,9 +537,6 @@ Indicate all the possible formats in which you will transmit your data outside o SequenceFlow_12bv2i4 SequenceFlow_1mnmo6p - - DateTransmittedOutside == False - #### Collection & storage of research data at UVA @@ -650,8 +618,8 @@ Submit the step only when you are ready. After you "Submit" the step, the inform - SequenceFlow_02pq2w4 Flow_1wgcnrc + Flow_1h5ufzp SequenceFlow_1wzlqa1 @@ -693,6 +661,7 @@ Submit the step only when you are ready. After you "Submit" the step, the inform FormField_DSP_Done == False + @@ -841,23 +810,12 @@ Submit the step only when you are ready. After you "Submit" the step, the inform - - - - - - - - + - - - - @@ -874,15 +832,6 @@ Submit the step only when you are ready. After you "Submit" the step, the inform - - - - - - - - - @@ -913,64 +862,68 @@ Submit the step only when you are ready. After you "Submit" the step, the inform - - + + - + - + - - + + - + - - + + - + - - - - + + + + - + - - + + - + - + - - + + - + - - - - + + + + - + + + + + diff --git a/crc/static/bpmn/crc2_training_session_enter_core_info/crc2_training_session_enter_core_info.bpmn b/crc/static/bpmn/crc2_training_session_enter_core_info/crc2_training_session_enter_core_info.bpmn index 15346b03..fc90a9de 100644 --- a/crc/static/bpmn/crc2_training_session_enter_core_info/crc2_training_session_enter_core_info.bpmn +++ b/crc/static/bpmn/crc2_training_session_enter_core_info/crc2_training_session_enter_core_info.bpmn @@ -14,6 +14,121 @@ + + SequenceFlow_0xfv8yt + SequenceFlow_1nyebxq + SequenceFlow_0s6lscl + SequenceFlow_0tl6dtl + SequenceFlow_1bdgyx8 + + + SequenceFlow_0vnit5w + SequenceFlow_12u08ph + SequenceFlow_1049qrj + SequenceFlow_1vfbg50 + SequenceFlow_0skynyx + + + SequenceFlow_0skynyx + + + SequenceFlow_0xfv8yt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFlow_0tl6dtl + SequenceFlow_1049qrj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFlow_1bdgyx8 + SequenceFlow_1vfbg50 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SequenceFlow_0s6lscl + SequenceFlow_12u08ph + @@ -83,163 +198,6 @@ SequenceFlow_1nyebxq SequenceFlow_0vnit5w - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SequenceFlow_0s6lscl - SequenceFlow_12u08ph - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SequenceFlow_1bdgyx8 - SequenceFlow_1vfbg50 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SequenceFlow_1oz7y6d - SequenceFlow_0vbmpyv - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SequenceFlow_0tl6dtl - SequenceFlow_1049qrj - - - SequenceFlow_0xfv8yt - SequenceFlow_1nyebxq - SequenceFlow_0s6lscl - SequenceFlow_0tl6dtl - SequenceFlow_1bdgyx8 - SequenceFlow_1oz7y6d - - - SequenceFlow_0vnit5w - SequenceFlow_12u08ph - SequenceFlow_1049qrj - SequenceFlow_1vfbg50 - SequenceFlow_0vbmpyv - SequenceFlow_0skynyx - - - SequenceFlow_0skynyx - - - SequenceFlow_0xfv8yt - @@ -250,13 +208,13 @@ - + - + - + @@ -268,7 +226,7 @@ - + @@ -276,63 +234,52 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - diff --git a/crc/static/bpmn/sponsor_funding_source/sponsor_funding_source.bpmn b/crc/static/bpmn/crc2_training_session_sponsor_funding_source/crc2_training_session_sponsor_funding_source.bpmn similarity index 78% rename from crc/static/bpmn/sponsor_funding_source/sponsor_funding_source.bpmn rename to crc/static/bpmn/crc2_training_session_sponsor_funding_source/crc2_training_session_sponsor_funding_source.bpmn index c3b9d2bd..bcf6f6d9 100644 --- a/crc/static/bpmn/sponsor_funding_source/sponsor_funding_source.bpmn +++ b/crc/static/bpmn/crc2_training_session_sponsor_funding_source/crc2_training_session_sponsor_funding_source.bpmn @@ -14,11 +14,7 @@ - - - - - + @@ -26,14 +22,7 @@ - - - - - - - - + @@ -51,10 +40,10 @@ - + - + diff --git a/example_data.py b/example_data.py index 793127c3..ddbcd9b0 100644 --- a/example_data.py +++ b/example_data.py @@ -10,6 +10,7 @@ from crc.models.user import UserModel from crc.models.workflow import WorkflowSpecModel from crc.services.file_service import FileService from crc.services.workflow_processor import WorkflowProcessor +from crc.models.protocol_builder import ProtocolBuilderStatus class ExampleDataLoader: @@ -41,7 +42,7 @@ class ExampleDataLoader: id=1, title='The impact of fried pickles on beer consumption in bipedal software developers.', last_updated=datetime.datetime.now(), - protocol_builder_status='in_process', + protocol_builder_status=ProtocolBuilderStatus.IN_PROCESS, primary_investigator_id='dhf8r', sponsor='Sartography Pharmaceuticals', ind_number='1234', @@ -51,7 +52,7 @@ class ExampleDataLoader: id=2, title='Requirement of hippocampal neurogenesis for the behavioral effects of soft pretzels', last_updated=datetime.datetime.now(), - protocol_builder_status='in_process', + protocol_builder_status=ProtocolBuilderStatus.IN_PROCESS, primary_investigator_id='dhf8r', sponsor='Makerspace & Co.', ind_number='5678', @@ -69,10 +70,11 @@ class ExampleDataLoader: name="crc2_training_session_data_security_plan", display_name="CR Connect2 - Training Session - Data Security Plan", description='Part of Milestone 3 Deliverable') - self.create_spec(id="sponsor_funding_source", - name="sponsor_funding_source", - display_name="Sponsor and/or Funding Source ", - description='TBD') + self.create_spec(id="crc2_training_session_sponsor_funding_source", + name="crc2_training_session_sponsor_funding_source", + display_name="CR Connect2 - Training Session - Sponsor and/or Funding Source", + description='Part of Milestone 3 Deliverable') + def create_spec(self, id, name, display_name="", description="", filepath=None): """Assumes that a directory exists in static/bpmn with the same name as the given id. diff --git a/migrations/versions/cb3a03c10a0e_.py b/migrations/versions/1c6e4e179f8e_.py similarity index 94% rename from migrations/versions/cb3a03c10a0e_.py rename to migrations/versions/1c6e4e179f8e_.py index 0280235c..b6237cb0 100644 --- a/migrations/versions/cb3a03c10a0e_.py +++ b/migrations/versions/1c6e4e179f8e_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: cb3a03c10a0e +Revision ID: 1c6e4e179f8e Revises: -Create Date: 2020-02-28 11:12:56.150837 +Create Date: 2020-03-03 15:51:45.550681 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'cb3a03c10a0e' +revision = '1c6e4e179f8e' down_revision = None branch_labels = None depends_on = None @@ -43,7 +43,7 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('title', sa.String(), nullable=True), sa.Column('last_updated', sa.DateTime(timezone=True), nullable=True), - sa.Column('protocol_builder_status', sa.Enum('out_of_date', 'in_process', 'complete', 'updating', name='protocolbuilderstatus'), nullable=True), + sa.Column('protocol_builder_status', sa.Enum('DRAFT', 'IN_PROCESS', 'IN_REVIEW', 'REVIEW_COMPLETE', 'INACTIVE', name='protocolbuilderstatus'), nullable=True), sa.Column('primary_investigator_id', sa.String(), nullable=True), sa.Column('sponsor', sa.String(), nullable=True), sa.Column('hsr_number', sa.String(), nullable=True), diff --git a/tests/data/pb_responses/user_studies.json b/tests/data/pb_responses/user_studies.json index 40aa65b3..18a0a00d 100644 --- a/tests/data/pb_responses/user_studies.json +++ b/tests/data/pb_responses/user_studies.json @@ -14,5 +14,13 @@ "Q_COMPLETE": true, "STUDYID": 65432, "TITLE": "Peanut butter consumption among quiet dogs" + }, + { + "DATE_MODIFIED": "2020-02-19T14:24:55.101695", + "HSRNUMBER": "45678", + "NETBADGEID": "dhf8r", + "Q_COMPLETE": true, + "STUDYID": 1, + "TITLE": "Efficacy of xenomorph bio-augmented circuits on dexterity of cybernetic prostheses" } ] diff --git a/tests/test_study_api.py b/tests/test_study_api.py index 136e38a4..d4ded939 100644 --- a/tests/test_study_api.py +++ b/tests/test_study_api.py @@ -1,11 +1,12 @@ import json from datetime import datetime, timezone -from unittest.mock import patch +from unittest.mock import patch, Mock from crc import session from crc.models.api_models import WorkflowApiSchema from crc.models.study import StudyModel, StudyModelSchema -from crc.models.protocol_builder import ProtocolBuilderStatus +from crc.models.protocol_builder import ProtocolBuilderStatus, ProtocolBuilderStudyDetailsSchema, \ + ProtocolBuilderStudySchema from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, WorkflowStatus from tests.base_test import BaseTest @@ -24,7 +25,7 @@ class TestStudyApi(BaseTest): "title": "Phase III Trial of Genuine People Personalities (GPP) Autonomous Intelligent Emotional Agents " "for Interstellar Spacecraft", "last_updated": datetime.now(tz=timezone.utc), - "protocol_builder_status": ProtocolBuilderStatus.in_process, + "protocol_builder_status": ProtocolBuilderStatus.IN_PROCESS, "primary_investigator_id": "tricia.marie.mcmillan@heartofgold.edu", "sponsor": "Sirius Cybernetics Corporation", "ind_number": "567890", @@ -49,7 +50,7 @@ class TestStudyApi(BaseTest): self.load_example_data() study: StudyModel = session.query(StudyModel).first() study.title = "Pilot Study of Fjord Placement for Single Fraction Outcomes to Cortisol Susceptibility" - study.protocol_builder_status = ProtocolBuilderStatus.complete + study.protocol_builder_status = ProtocolBuilderStatus.REVIEW_COMPLETE rv = self.app.put('/v1.0/study/%i' % study.id, content_type="application/json", headers=self.logged_in_headers(), @@ -60,17 +61,19 @@ class TestStudyApi(BaseTest): self.assertEqual(study.title, db_study.title) self.assertEqual(study.protocol_builder_status, db_study.protocol_builder_status) - - @patch('crc.services.protocol_builder.requests.get') - def test_get_all_studies(self, mock_get): + @patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details + @patch('crc.services.protocol_builder.ProtocolBuilderService.get_studies') # mock_studies + def test_get_all_studies(self, mock_studies, mock_details): self.load_example_data() db_studies_before = session.query(StudyModel).all() num_db_studies_before = len(db_studies_before) - mock_get.return_value.ok = True - mock_get.return_value.text = self.protocol_builder_response('user_studies.json') - # pb_response = ProtocolBuilderService.get_studies(self.test_uid) - # self.assertIsNotNone(pb_response) + # Mock Protocol Builder response + studies_response = self.protocol_builder_response('user_studies.json') + mock_studies.return_value = ProtocolBuilderStudySchema(many=True).loads(studies_response) + + details_response = self.protocol_builder_response('study_details.json') + mock_details.return_value = ProtocolBuilderStudyDetailsSchema().loads(details_response) self.load_example_data() api_response = self.app.get('/v1.0/study', @@ -78,11 +81,6 @@ class TestStudyApi(BaseTest): headers=self.logged_in_headers(), content_type="application/json") self.assert_success(api_response) - - db_studies_after = session.query(StudyModel).all() - num_db_studies_after = len(db_studies_after) - self.assertGreater(num_db_studies_after, num_db_studies_before) - json_data = json.loads(api_response.get_data(as_text=True)) api_studies = StudyModelSchema(many=True).load(json_data, session=session) @@ -95,8 +93,13 @@ class TestStudyApi(BaseTest): else: num_active += 1 - self.assertEqual(num_inactive, num_db_studies_before) - self.assertEqual(num_active, num_db_studies_after - num_db_studies_before) + db_studies_after = session.query(StudyModel).all() + num_db_studies_after = len(db_studies_after) + self.assertGreater(num_db_studies_after, num_db_studies_before) + self.assertGreater(num_inactive, 0) + self.assertGreater(num_active, 0) + self.assertEqual(len(api_studies), num_db_studies_after) + self.assertEqual(num_active + num_inactive, num_db_studies_after) def test_study_api_get_single_study(self): self.load_example_data()