Merge pull request #324 from sartography/modify-infinite-loop-312
Modify infinite loop #312
This commit is contained in:
commit
9c4994581d
|
@ -512,6 +512,12 @@ paths:
|
|||
description: The unique id of an existing workflow specification to validate.
|
||||
schema:
|
||||
type: string
|
||||
- name: validate_study_id
|
||||
in: query
|
||||
required: false
|
||||
description: Optional id of study to test under different scenarios
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
operationId: crc.api.workflow.validate_workflow_specification
|
||||
summary: Loads and attempts to execute a Workflow Specification, returning a list of errors encountered
|
||||
|
|
|
@ -46,16 +46,16 @@ def get_workflow_specification(spec_id):
|
|||
return WorkflowSpecModelSchema().dump(spec)
|
||||
|
||||
|
||||
def validate_workflow_specification(spec_id):
|
||||
def validate_workflow_specification(spec_id, validate_study_id=None):
|
||||
errors = {}
|
||||
try:
|
||||
WorkflowService.test_spec(spec_id)
|
||||
WorkflowService.test_spec(spec_id, validate_study_id)
|
||||
except ApiError as ae:
|
||||
ae.message = "When populating all fields ... \n" + ae.message
|
||||
errors['all'] = ae
|
||||
try:
|
||||
# Run the validation twice, the second time, just populate the required fields.
|
||||
WorkflowService.test_spec(spec_id, required_only=True)
|
||||
WorkflowService.test_spec(spec_id, validate_study_id, required_only=True)
|
||||
except ApiError as ae:
|
||||
ae.message = "When populating only required fields ... \n" + ae.message
|
||||
errors['required'] = ae
|
||||
|
|
|
@ -4,9 +4,10 @@ from SpiffWorkflow.bpmn.PythonScriptEngine import Box
|
|||
|
||||
from crc import session
|
||||
from crc.api.common import ApiError
|
||||
from crc.api.workflow import get_workflow
|
||||
from crc.models.protocol_builder import ProtocolBuilderInvestigatorType
|
||||
from crc.models.study import StudyModel, StudySchema
|
||||
from crc.models.workflow import WorkflowStatus
|
||||
from crc.api import workflow as workflow_api
|
||||
from crc.scripts.script import Script
|
||||
from crc.services.cache_service import timeit
|
||||
from crc.services.file_service import FileService
|
||||
|
@ -169,197 +170,10 @@ Please note this is just a few examples, ALL known document types are returned i
|
|||
# Assure the reference file exists (a bit hacky, but we want to raise this error early, and cleanly.)
|
||||
FileService.get_reference_file_data(FileService.DOCUMENT_LIST)
|
||||
FileService.get_reference_file_data(FileService.INVESTIGATOR_LIST)
|
||||
data = {
|
||||
"study": {
|
||||
"info": {
|
||||
"id": 12,
|
||||
"title": "test",
|
||||
"short_title": "tst",
|
||||
"primary_investigator_id": 21,
|
||||
"user_uid": "dif84",
|
||||
"sponsor": "sponsor",
|
||||
"ind_number": "1234",
|
||||
"inactive": False
|
||||
},
|
||||
"sponsors": [
|
||||
{
|
||||
"COMMONRULEAGENCY": None,
|
||||
"SPONSOR_ID": 2453,
|
||||
"SP_NAME": "Abbott Ltd",
|
||||
"SP_TYPE": "Private",
|
||||
"SP_TYPE_GROUP_NAME": None,
|
||||
"SS_STUDY": 2
|
||||
},
|
||||
{
|
||||
"COMMONRULEAGENCY": None,
|
||||
"SPONSOR_ID": 2387,
|
||||
"SP_NAME": "Abbott-Price",
|
||||
"SP_TYPE": "Incoming Sub Award",
|
||||
"SP_TYPE_GROUP_NAME": "Government",
|
||||
"SS_STUDY": 2
|
||||
},
|
||||
{
|
||||
"COMMONRULEAGENCY": None,
|
||||
"SPONSOR_ID": 1996,
|
||||
"SP_NAME": "Abernathy-Heidenreich",
|
||||
"SP_TYPE": "Foundation/Not for Profit",
|
||||
"SP_TYPE_GROUP_NAME": "Other External Funding",
|
||||
"SS_STUDY": 2
|
||||
}
|
||||
],
|
||||
|
||||
"investigators": {
|
||||
"PI": {
|
||||
"label": ProtocolBuilderInvestigatorType.PI.value,
|
||||
"display": "Always",
|
||||
"unique": "Yes",
|
||||
"user_id": "dhf8r",
|
||||
"title": "",
|
||||
"display_name": "Daniel Harold Funk",
|
||||
"sponsor_type": "Contractor",
|
||||
"telephone_number": "0000000000",
|
||||
"department": "",
|
||||
"email_address": "dhf8r@virginia.edu",
|
||||
"given_name": "Daniel",
|
||||
"uid": "dhf8r",
|
||||
"affiliation": "",
|
||||
"date_cached": "2020-08-04T19:32:08.006128+00:00"
|
||||
},
|
||||
"SC_I": {
|
||||
"label": ProtocolBuilderInvestigatorType.SC_I.value,
|
||||
"display": "Always",
|
||||
"unique": "Yes",
|
||||
"user_id": "ajl2j",
|
||||
"title": "",
|
||||
"display_name": "Aaron Louie",
|
||||
"sponsor_type": "Contractor",
|
||||
"telephone_number": "0000000000",
|
||||
"department": "",
|
||||
"email_address": "ajl2j@virginia.edu",
|
||||
"given_name": "Aaron",
|
||||
"uid": "ajl2j",
|
||||
"affiliation": "sponsored",
|
||||
"date_cached": "2020-08-04T19:32:10.699666+00:00"
|
||||
},
|
||||
"SC_II": {
|
||||
"label": ProtocolBuilderInvestigatorType.SC_II.value,
|
||||
"display": "Optional",
|
||||
"unique": "Yes",
|
||||
"user_id": "cah3us",
|
||||
"title": "",
|
||||
"display_name": "Alex Herron",
|
||||
"sponsor_type": "Contractor",
|
||||
"telephone_number": "0000000000",
|
||||
"department": "",
|
||||
"email_address": "cah3us@virginia.edu",
|
||||
"given_name": "Alex",
|
||||
"uid": "cah3us",
|
||||
"affiliation": "sponsored",
|
||||
"date_cached": "2020-08-04T19:32:10.075852+00:00"
|
||||
},
|
||||
},
|
||||
"pi": {
|
||||
"PI": {
|
||||
"label": ProtocolBuilderInvestigatorType.PI.value,
|
||||
"display": "Always",
|
||||
"unique": "Yes",
|
||||
"user_id": "dhf8r",
|
||||
"title": "",
|
||||
"display_name": "Daniel Harold Funk",
|
||||
"sponsor_type": "Contractor",
|
||||
"telephone_number": "0000000000",
|
||||
"department": "",
|
||||
"email_address": "dhf8r@virginia.edu",
|
||||
"given_name": "Daniel",
|
||||
"uid": "dhf8r",
|
||||
"affiliation": "",
|
||||
"date_cached": "2020-08-04T19:32:08.006128+00:00"
|
||||
}
|
||||
},
|
||||
"roles":
|
||||
{
|
||||
"INVESTIGATORTYPE": "PI",
|
||||
"INVESTIGATORTYPEFULL": ProtocolBuilderInvestigatorType.PI.value,
|
||||
"NETBADGEID": "dhf8r"
|
||||
},
|
||||
"details":
|
||||
{
|
||||
"DSMB": None,
|
||||
"DSMB_FREQUENCY": None,
|
||||
"GCRC_NUMBER": None,
|
||||
"IBC_NUMBER": None,
|
||||
"IDE": None,
|
||||
"IND_1": 1234,
|
||||
"IND_2": None,
|
||||
"IND_3": None,
|
||||
"IRBREVIEWERADMIN": None,
|
||||
"IS_ADULT_PARTICIPANT": None,
|
||||
"IS_APPROVED_DEVICE": None,
|
||||
"IS_AUX": None,
|
||||
"IS_BIOMEDICAL": None,
|
||||
"IS_CANCER_PATIENT": None,
|
||||
"IS_CENTRAL_REG_DB": None,
|
||||
"IS_CHART_REVIEW": None,
|
||||
"IS_COMMITTEE_CONFLICT": None,
|
||||
"IS_CONSENT_WAIVER": None,
|
||||
"IS_DB": None,
|
||||
"IS_ELDERLY_POP": None,
|
||||
"IS_ENGAGED_RESEARCH": None,
|
||||
"IS_FETUS_POP": None,
|
||||
"IS_FINANCIAL_CONFLICT": None,
|
||||
"IS_FOR_CANCER_CENTER": None,
|
||||
"IS_FUNDING_SOURCE": None,
|
||||
"IS_GCRC": None,
|
||||
"IS_GENE_TRANSFER": None,
|
||||
"IS_GRANT": None,
|
||||
"IS_HGT": None,
|
||||
"IS_IBC": None,
|
||||
"IS_IDE": None,
|
||||
"IS_IND": 1,
|
||||
"IS_MENTAL_IMPAIRMENT_POP": None,
|
||||
"IS_MINOR": None,
|
||||
"IS_MINOR_PARTICIPANT": None,
|
||||
"IS_MULTI_SITE": None,
|
||||
"IS_NOT_CONSENT_WAIVER": None,
|
||||
"IS_NOT_PRC_WAIVER": None,
|
||||
"IS_OTHER_VULNERABLE_POP": None,
|
||||
"IS_OUTSIDE_CONTRACT": None,
|
||||
"IS_PI_INITIATED": None,
|
||||
"IS_PI_SCHOOL": None,
|
||||
"IS_PRC": None,
|
||||
"IS_PRC_DSMP": None,
|
||||
"IS_PREGNANT_POP": None,
|
||||
"IS_PRISONERS_POP": None,
|
||||
"IS_QUALITATIVE": None,
|
||||
"IS_RADIATION": None,
|
||||
"IS_REVIEW_BY_CENTRAL_IRB": None,
|
||||
"IS_SPONSOR": None,
|
||||
"IS_SPONSOR_MONITORING": None,
|
||||
"IS_SURROGATE_CONSENT": None,
|
||||
"IS_TISSUE_BANKING": None,
|
||||
"IS_UVA_DB": None,
|
||||
"IS_UVA_IDE": None,
|
||||
"IS_UVA_IND": None,
|
||||
"IS_UVA_LOCATION": None,
|
||||
"IS_UVA_PI_MULTI": None,
|
||||
"MULTI_SITE_LOCATIONS": None,
|
||||
"NON_UVA_LOCATION": None,
|
||||
"OTHER_VULNERABLE_DESC": None,
|
||||
"PRC_NUMBER": None,
|
||||
"SPONSORS_PROTOCOL_REVISION_DATE": None,
|
||||
"UPLOAD_COMPLETE": None
|
||||
},
|
||||
'protocol': {
|
||||
'id': 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
if args[0] == 'documents':
|
||||
return self.box_it(StudyService().get_documents_status(study_id))
|
||||
return self.box_it(data['study'][args[0]])
|
||||
|
||||
# self.add_data_to_task(task=task, data=data["study"])
|
||||
# self.add_data_to_task(task, {"documents": StudyService().get_documents_status(study_id)})
|
||||
# we call the real do_task so we can
|
||||
# seed workflow validations with settings from studies in PB Mock
|
||||
# in order to test multiple paths thru the workflow
|
||||
return self.do_task(task, study_id, workflow_id, args[0])
|
||||
|
||||
@timeit
|
||||
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
|
|
|
@ -52,17 +52,27 @@ class WorkflowService(object):
|
|||
handles the testing of a workflow specification by completing it with
|
||||
random selections, attempting to mimic a front end as much as possible. """
|
||||
|
||||
from crc.services.user_service import UserService
|
||||
@staticmethod
|
||||
def make_test_workflow(spec_id):
|
||||
user = db.session.query(UserModel).filter_by(uid="test").first()
|
||||
def make_test_workflow(spec_id, validate_study_id=None):
|
||||
try:
|
||||
user = UserService.current_user()
|
||||
except ApiError as e:
|
||||
user = None
|
||||
if not user:
|
||||
user = db.session.query(UserModel).filter_by(uid="test").first()
|
||||
if not user:
|
||||
db.session.add(UserModel(uid="test"))
|
||||
db.session.commit()
|
||||
study = db.session.query(StudyModel).filter_by(user_uid="test").first()
|
||||
user = db.session.query(UserModel).filter_by(uid="test").first()
|
||||
if validate_study_id:
|
||||
study = db.session.query(StudyModel).filter_by(id=validate_study_id).first()
|
||||
else:
|
||||
study = db.session.query(StudyModel).filter_by(user_uid=user.uid).first()
|
||||
if not study:
|
||||
db.session.add(StudyModel(user_uid="test", title="test"))
|
||||
db.session.add(StudyModel(user_uid=user.uid, title="test"))
|
||||
db.session.commit()
|
||||
study = db.session.query(StudyModel).filter_by(user_uid="test").first()
|
||||
study = db.session.query(StudyModel).filter_by(user_uid=user.uid).first()
|
||||
workflow_model = WorkflowModel(status=WorkflowStatus.not_started,
|
||||
workflow_spec_id=spec_id,
|
||||
last_updated=datetime.utcnow(),
|
||||
|
@ -80,7 +90,7 @@ class WorkflowService(object):
|
|||
db.session.delete(user)
|
||||
|
||||
@staticmethod
|
||||
def test_spec(spec_id, required_only=False):
|
||||
def test_spec(spec_id, validate_study_id=None, required_only=False):
|
||||
"""Runs a spec through it's paces to see if it results in any errors.
|
||||
Not fool-proof, but a good sanity check. Returns the final data
|
||||
output form the last task if successful.
|
||||
|
@ -89,7 +99,7 @@ class WorkflowService(object):
|
|||
spec, only completing the required fields, rather than everything.
|
||||
"""
|
||||
|
||||
workflow_model = WorkflowService.make_test_workflow(spec_id)
|
||||
workflow_model = WorkflowService.make_test_workflow(spec_id, validate_study_id)
|
||||
|
||||
try:
|
||||
processor = WorkflowProcessor(workflow_model, validate_only=True)
|
||||
|
|
|
@ -19,7 +19,11 @@ class TestSudySponsorsScript(BaseTest):
|
|||
test_study_id = 1
|
||||
|
||||
|
||||
def test_study_sponsors_script_validation(self):
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_study_sponsors_script_validation(self, mock_get):
|
||||
app.config['PB_ENABLED'] = True
|
||||
mock_get.return_value.ok = True
|
||||
mock_get.return_value.text = self.protocol_builder_response('sponsors.json')
|
||||
flask.g.user = UserModel(uid='dhf8r')
|
||||
self.load_example_data() # study_info script complains if irb_documents.xls is not loaded
|
||||
# during the validate phase I'm going to assume that we will never
|
||||
|
|
|
@ -16,7 +16,11 @@ class TestSudySponsorsScript(BaseTest):
|
|||
test_study_id = 1
|
||||
|
||||
|
||||
def test_study_sponsors_script_validation(self):
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_study_sponsors_script_validation(self, mock_get):
|
||||
mock_get.return_value.ok = True
|
||||
mock_get.return_value.text = self.protocol_builder_response('sponsors.json')
|
||||
app.config['PB_ENABLED'] = True
|
||||
flask.g.user = UserModel(uid='dhf8r')
|
||||
self.load_example_data() # study_info script complains if irb_documents.xls is not loaded
|
||||
# during the validate phase I'm going to assume that we will never
|
||||
|
|
|
@ -5,7 +5,7 @@ from SpiffWorkflow.bpmn.PythonScriptEngine import Box
|
|||
from tests.base_test import BaseTest
|
||||
from unittest.mock import patch
|
||||
|
||||
from crc import db, session
|
||||
from crc import app, session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileDataModel, FileModel
|
||||
from crc.models.protocol_builder import ProtocolBuilderRequiredDocumentSchema, ProtocolBuilderStudySchema
|
||||
|
@ -29,8 +29,23 @@ class TestStudyDetailsScript(BaseTest):
|
|||
self.processor = WorkflowProcessor(self.workflow_model)
|
||||
self.task = self.processor.next_task()
|
||||
|
||||
def test_study_info_returns_a_box_object_for_all_validations(self):
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_study_info_returns_a_box_object_for_all_validations(self, mock_get):
|
||||
app.config['PB_ENABLED'] = True
|
||||
mock_get.return_value.ok = True
|
||||
for option in StudyInfo.type_options:
|
||||
if option == 'info':
|
||||
mock_get.return_value.text = self.protocol_builder_response('irb_info.json')
|
||||
elif option == 'investigators':
|
||||
mock_get.return_value.text = self.protocol_builder_response('investigators.json')
|
||||
elif option == 'roles':
|
||||
mock_get.return_value.text = self.protocol_builder_response('investigators.json')
|
||||
elif option == 'details':
|
||||
mock_get.return_value.text = self.protocol_builder_response('study_details.json')
|
||||
elif option == 'documents':
|
||||
mock_get.return_value.text = self.protocol_builder_response('required_docs.json')
|
||||
elif option == 'sponsors':
|
||||
mock_get.return_value.text = self.protocol_builder_response('sponsors.json')
|
||||
data = StudyInfo().do_task_validate_only(self.task, self.study.id, self.workflow_model.id, option)
|
||||
if isinstance(data, list):
|
||||
for x in data:
|
||||
|
|
|
@ -13,7 +13,12 @@ class TestSudySponsorsScript(BaseTest):
|
|||
test_study_id = 1
|
||||
|
||||
|
||||
def test_study_sponsors_script_validation(self):
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_study_sponsors_script_validation(self, mock_get):
|
||||
mock_get.return_value.ok = True
|
||||
mock_get.return_value.text = self.protocol_builder_response('sponsors.json')
|
||||
app.config['PB_ENABLED'] = True
|
||||
|
||||
self.load_example_data() # study_info script complains if irb_documents.xls is not loaded
|
||||
# during the validate phase I'm going to assume that we will never
|
||||
# have a case where irb_documents.xls is not loaded ??
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
from tests.base_test import BaseTest
|
||||
from crc import app
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
from crc.api.common import ApiError
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestValidateEndEvent(BaseTest):
|
||||
|
||||
def test_validate_end_event(self):
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_validate_end_event(self, mock_get):
|
||||
app.config['PB_ENABLED'] = True
|
||||
mock_get.return_value.ok = True
|
||||
mock_get.return_value.text = self.protocol_builder_response('study_details.json')
|
||||
|
||||
error_string = """Error processing template for task EndEvent_1qvyxg7: expected token 'end of statement block', got '='"""
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
from tests.base_test import BaseTest
|
||||
from crc import app
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestWorkflowInfiniteLoop(BaseTest):
|
||||
|
||||
def test_workflow_infinite_loop(self):
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_workflow_infinite_loop(self, mock_get):
|
||||
app.config['PB_ENABLED'] = True
|
||||
mock_get.return_value.ok = True
|
||||
mock_get.return_value.text = self.protocol_builder_response('investigators.json')
|
||||
self.load_example_data()
|
||||
spec_model = self.load_test_spec('infinite_loop')
|
||||
rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers())
|
||||
|
|
Loading…
Reference in New Issue