Merge pull request #324 from sartography/modify-infinite-loop-312

Modify infinite loop #312
This commit is contained in:
Dan Funk 2021-06-08 10:30:13 -04:00 committed by GitHub
commit 9c4994581d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 78 additions and 210 deletions

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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 ??

View File

@ -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 '='"""

View File

@ -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())