Merge pull request #339 from sartography/restrict-loaded-studies-374
Restrict loaded studies #374
This commit is contained in:
commit
d1bf370a39
|
@ -94,7 +94,7 @@ def user_studies():
|
|||
"""Returns all the studies associated with the current user. """
|
||||
user = UserService.current_user(allow_admin_impersonate=True)
|
||||
StudyService.synch_with_protocol_builder_if_enabled(user)
|
||||
studies = StudyService.get_studies_for_user(user)
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
results = StudySchema(many=True).dump(studies)
|
||||
return results
|
||||
|
||||
|
|
|
@ -29,12 +29,19 @@ from crc.services.lookup_service import LookupService
|
|||
from crc.services.protocol_builder import ProtocolBuilderService
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
|
||||
|
||||
class StudyService(object):
|
||||
"""Provides common tools for working with a Study"""
|
||||
INVESTIGATOR_LIST = "investigators.xlsx" # A reference document containing details about what investigators to show, and when.
|
||||
|
||||
@staticmethod
|
||||
def get_studies_for_user(user):
|
||||
def _is_valid_study(study_id):
|
||||
study_info = ProtocolBuilderService().get_study_details(study_id)
|
||||
if 'REVIEW_TYPE' in study_info.keys() and study_info['REVIEW_TYPE'] in [2, 3, 23, 24]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_studies_for_user(self, user):
|
||||
"""Returns a list of all studies for the given user."""
|
||||
associated = session.query(StudyAssociated).filter_by(uid=user.uid,access=True).all()
|
||||
associated_studies = [x.study_id for x in associated]
|
||||
|
@ -43,7 +50,8 @@ class StudyService(object):
|
|||
|
||||
studies = []
|
||||
for study_model in db_studies:
|
||||
studies.append(StudyService.get_study(study_model.id, study_model,do_status=False))
|
||||
if self._is_valid_study(study_model.id):
|
||||
studies.append(StudyService.get_study(study_model.id, study_model,do_status=False))
|
||||
return studies
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -62,5 +62,6 @@
|
|||
"OTHER_VULNERABLE_DESC": null,
|
||||
"PRC_NUMBER": null,
|
||||
"SPONSORS_PROTOCOL_REVISION_DATE": "2021-04-20",
|
||||
"UPLOAD_COMPLETE": null
|
||||
"UPLOAD_COMPLETE": null,
|
||||
"REVIEW_TYPE": 2
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"DSMB": 1,
|
||||
"DSMB_FREQUENCY": 2,
|
||||
"GCRC_NUMBER": "9",
|
||||
"IBC_NUMBER": "7",
|
||||
"IDE": "12345",
|
||||
"IND_1": "1234",
|
||||
"IND_2": "2345",
|
||||
"IND_3": "3456",
|
||||
"IRBREVIEWERADMIN": null,
|
||||
"IS_ADULT_PARTICIPANT": null,
|
||||
"IS_APPROVED_DEVICE": null,
|
||||
"IS_AUX": null,
|
||||
"IS_BIOMEDICAL": null,
|
||||
"IS_CANCER_PATIENT": null,
|
||||
"IS_CENTRAL_REG_DB": null,
|
||||
"IS_CHART_REVIEW": null,
|
||||
"IS_COMMITTEE_CONFLICT": null,
|
||||
"IS_CONSENT_WAIVER": null,
|
||||
"IS_DB": null,
|
||||
"IS_ELDERLY_POP": null,
|
||||
"IS_ENGAGED_RESEARCH": null,
|
||||
"IS_FETUS_POP": null,
|
||||
"IS_FINANCIAL_CONFLICT": null,
|
||||
"IS_FOR_CANCER_CENTER": null,
|
||||
"IS_FUNDING_SOURCE": null,
|
||||
"IS_GCRC": null,
|
||||
"IS_GENE_TRANSFER": null,
|
||||
"IS_GRANT": null,
|
||||
"IS_HGT": null,
|
||||
"IS_IBC": null,
|
||||
"IS_IDE": null,
|
||||
"IS_IND": null,
|
||||
"IS_MENTAL_IMPAIRMENT_POP": null,
|
||||
"IS_MINOR": null,
|
||||
"IS_MINOR_PARTICIPANT": null,
|
||||
"IS_MULTI_SITE": null,
|
||||
"IS_NOT_CONSENT_WAIVER": null,
|
||||
"IS_NOT_PRC_WAIVER": null,
|
||||
"IS_OTHER_VULNERABLE_POP": null,
|
||||
"IS_OUTSIDE_CONTRACT": null,
|
||||
"IS_PI_INITIATED": null,
|
||||
"IS_PI_SCHOOL": null,
|
||||
"IS_PRC": null,
|
||||
"IS_PRC_DSMP": null,
|
||||
"IS_PREGNANT_POP": null,
|
||||
"IS_PRISONERS_POP": null,
|
||||
"IS_QUALITATIVE": null,
|
||||
"IS_RADIATION": null,
|
||||
"IS_REVIEW_BY_CENTRAL_IRB": null,
|
||||
"IS_SPONSOR": null,
|
||||
"IS_SPONSOR_MONITORING": null,
|
||||
"IS_SURROGATE_CONSENT": null,
|
||||
"IS_TISSUE_BANKING": null,
|
||||
"IS_UVA_DB": null,
|
||||
"IS_UVA_IDE": null,
|
||||
"IS_UVA_IND": null,
|
||||
"IS_UVA_LOCATION": null,
|
||||
"IS_UVA_PI_MULTI": null,
|
||||
"MULTI_SITE_LOCATIONS": null,
|
||||
"NON_UVA_LOCATION": null,
|
||||
"OTHER_VULNERABLE_DESC": null,
|
||||
"PRC_NUMBER": null,
|
||||
"SPONSORS_PROTOCOL_REVISION_DATE": "2021-04-20",
|
||||
"UPLOAD_COMPLETE": null,
|
||||
"REVIEW_TYPE": 99
|
||||
}
|
|
@ -2,6 +2,7 @@ from tests.base_test import BaseTest
|
|||
from crc import session
|
||||
from crc.models.study import StudyModel, StudyStatus, StudySchema
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestStudyActionsStatus(BaseTest):
|
||||
|
@ -20,8 +21,11 @@ class TestStudyActionsStatus(BaseTest):
|
|||
study_result = session.query(StudyModel).filter(StudyModel.id == study.id).first()
|
||||
return study_result
|
||||
|
||||
def test_hold_study(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_hold_study(self, mock_details):
|
||||
self.load_example_data()
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
study = session.query(StudyModel).first()
|
||||
self.assertEqual(study.status, StudyStatus.in_progress)
|
||||
|
@ -33,8 +37,11 @@ class TestStudyActionsStatus(BaseTest):
|
|||
study_result = self.update_study_status(study, study_schema)
|
||||
self.assertEqual(StudyStatus.hold, study_result.status)
|
||||
|
||||
def test_abandon_study(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_abandon_study(self, mock_details):
|
||||
self.load_example_data()
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
study = session.query(StudyModel).first()
|
||||
self.assertEqual(study.status, StudyStatus.in_progress)
|
||||
|
@ -46,8 +53,11 @@ class TestStudyActionsStatus(BaseTest):
|
|||
study_result = self.update_study_status(study, study_schema)
|
||||
self.assertEqual(StudyStatus.abandoned, study_result.status)
|
||||
|
||||
def test_open_enrollment_study(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_open_enrollment_study(self, mock_details):
|
||||
self.load_example_data()
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
study = session.query(StudyModel).first()
|
||||
self.assertEqual(study.status, StudyStatus.in_progress)
|
||||
|
|
|
@ -138,10 +138,13 @@ class TestSudySponsorsScript(BaseTest):
|
|||
# who is allowed access
|
||||
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_study_sponsors_script_ensure_access(self, mock_get):
|
||||
def test_study_sponsors_script_ensure_access(self, mock_get, mock_details):
|
||||
mock_get.return_value.ok = True
|
||||
mock_get.return_value.text = self.protocol_builder_response('sponsors.json')
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
flask.g.user = UserModel(uid='dhf8r')
|
||||
app.config['PB_ENABLED'] = True
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from crc.models.study import StudyModel, StudySchema
|
|||
from crc.models.workflow import WorkflowModel, WorkflowSpecModel
|
||||
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestStudyCancellations(BaseTest):
|
||||
|
@ -60,7 +61,10 @@ class TestStudyCancellations(BaseTest):
|
|||
self.assertEqual('Activity_Modify', third_task.name)
|
||||
return workflow_api, third_task
|
||||
|
||||
def test_before_cancel(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_before_cancel(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
workflow, study_id = self.load_workflow()
|
||||
self.get_first_task(workflow)
|
||||
|
@ -68,7 +72,10 @@ class TestStudyCancellations(BaseTest):
|
|||
study_result = self.put_study_on_hold(study_id)
|
||||
self.assertEqual('Beer consumption in the bipedal software engineer', study_result.title)
|
||||
|
||||
def test_first_cancel(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_first_cancel(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
workflow, study_id = self.load_workflow()
|
||||
workflow_api, first_task = self.get_first_task(workflow)
|
||||
|
||||
|
@ -77,7 +84,10 @@ class TestStudyCancellations(BaseTest):
|
|||
study_result = self.put_study_on_hold(study_id)
|
||||
self.assertEqual('New Title', study_result.title)
|
||||
|
||||
def test_second_cancel(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_second_cancel(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
workflow, study_id = self.load_workflow()
|
||||
workflow_api, first_task = self.get_first_task(workflow)
|
||||
|
@ -90,7 +100,10 @@ class TestStudyCancellations(BaseTest):
|
|||
study_result = self.put_study_on_hold(study_id)
|
||||
self.assertEqual('Second Title', study_result.title)
|
||||
|
||||
def test_after_cancel(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_after_cancel(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
workflow, study_id = self.load_workflow()
|
||||
workflow_api, first_task = self.get_first_task(workflow)
|
||||
|
|
|
@ -54,17 +54,20 @@ class TestStudyService(BaseTest):
|
|||
ExampleDataLoader().load_reference_documents()
|
||||
return user
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||
def test_total_tasks_updated(self, mock_docs):
|
||||
def test_total_tasks_updated(self, mock_docs, mock_details):
|
||||
"""Assure that as a users progress is available when getting a list of studies for that user."""
|
||||
app.config['PB_ENABLED'] = True
|
||||
docs_response = self.protocol_builder_response('required_docs.json')
|
||||
mock_docs.return_value = json.loads(docs_response)
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
user = self.create_user_with_study_and_workflow()
|
||||
|
||||
# The load example data script should set us up a user and at least one study, one category, and one workflow.
|
||||
studies = StudyService.get_studies_for_user(user)
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
self.assertTrue(len(studies) == 1)
|
||||
self.assertTrue(len(studies[0].categories) == 1)
|
||||
self.assertTrue(len(studies[0].categories[0].workflows) == 1)
|
||||
|
@ -82,7 +85,7 @@ class TestStudyService(BaseTest):
|
|||
processor.do_engine_steps()
|
||||
|
||||
# Assure the workflow is now started, and knows the total and completed tasks.
|
||||
studies = StudyService.get_studies_for_user(user)
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
workflow = next(iter(studies[0].categories[0].workflows)) # Workflows is a set.
|
||||
# self.assertEqual(WorkflowStatus.user_input_required, workflow.status)
|
||||
self.assertTrue(workflow.total_tasks > 0)
|
||||
|
@ -95,21 +98,24 @@ class TestStudyService(BaseTest):
|
|||
processor.save()
|
||||
|
||||
# Assure the workflow has moved on to the next task.
|
||||
studies = StudyService.get_studies_for_user(user)
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
workflow = next(iter(studies[0].categories[0].workflows)) # Workflows is a set.
|
||||
self.assertEqual(1, workflow.completed_tasks)
|
||||
|
||||
# Get approvals
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||
def test_get_required_docs(self, mock_docs):
|
||||
def test_get_required_docs(self, mock_docs, mock_details):
|
||||
app.config['PB_ENABLED'] = True
|
||||
# mock out the protocol builder
|
||||
docs_response = self.protocol_builder_response('required_docs.json')
|
||||
mock_docs.return_value = json.loads(docs_response)
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
user = self.create_user_with_study_and_workflow()
|
||||
studies = StudyService.get_studies_for_user(user)
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
study = studies[0]
|
||||
|
||||
|
||||
|
@ -227,3 +233,23 @@ class TestStudyService(BaseTest):
|
|||
# Both Alex and Aaron are SI, and both should be returned.
|
||||
self.assertEqual("ajl2j", investigators['SI']['user_id'])
|
||||
self.assertEqual("cah3us", investigators['SI_2']['user_id'])
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_get_user_studies(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
user = self.create_user_with_study_and_workflow()
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
# study_details has a valid REVIEW_TYPE, so we should get 1 study back
|
||||
self.assertEqual(1, len(studies))
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_get_user_studies_bad_review_type(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details_bad_review_type.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
|
||||
user = self.create_user_with_study_and_workflow()
|
||||
studies = StudyService().get_studies_for_user(user)
|
||||
# study_details has an invalid REVIEW_TYPE, so we should get 0 studies back
|
||||
self.assertEqual(0, len(studies))
|
||||
|
|
|
@ -11,6 +11,8 @@ from crc.models.protocol_builder import ProtocolBuilderStatus
|
|||
from crc.models.study import StudySchema, StudyModel, StudyStatus
|
||||
from crc.models.user import UserModel
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestAuthentication(BaseTest):
|
||||
admin_uid = 'dhf8r'
|
||||
|
@ -204,7 +206,10 @@ class TestAuthentication(BaseTest):
|
|||
user_data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertEqual(len(user_data), len(all_users))
|
||||
|
||||
def test_admin_can_impersonate_another_user(self):
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_study_details') # mock_details
|
||||
def test_admin_can_impersonate_another_user(self, mock_details):
|
||||
details_response = self.protocol_builder_response('study_details.json')
|
||||
mock_details.return_value = json.loads(details_response)
|
||||
# Switch production mode on
|
||||
app.config['PRODUCTION'] = True
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ class TestProtocolBuilder(BaseTest):
|
|||
mock_get.return_value.text = self.protocol_builder_response('study_details.json')
|
||||
response = ProtocolBuilderService.get_study_details(self.test_study_id)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(64, len(response))
|
||||
self.assertEqual(65, len(response))
|
||||
self.assertEqual('1234', response['IND_1'])
|
||||
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
|
|
Loading…
Reference in New Issue