Merge pull request #474 from sartography/dev

dev to master
This commit is contained in:
Dan Funk 2022-02-25 16:21:09 -05:00 committed by GitHub
commit 9c098717da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 250 additions and 31 deletions

View File

@ -13,7 +13,7 @@ alembic = "*"
coverage = "*"
docxtpl = "*"
flask = "*"
celery = "<5"
celery = "<6"
flask-admin = "*"
flask-bcrypt = "*"
flask-cors = "*"

83
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "eb924ba10433552dcde585064a905bb8b78e96b28fc23391eee486ca34e22aad"
"sha256": "89204202883b76908f866ebe652cb214c079575f87cb05f677d602e3f9040c4b"
},
"pipfile-spec": 6,
"requires": {
@ -33,11 +33,11 @@
},
"amqp": {
"hashes": [
"sha256:70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21",
"sha256:aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59"
"sha256:1e5f707424e544078ca196e72ae6a14887ce74e02bd126be54b7c03c971bef18",
"sha256:9cd81f7b023fc04bbb108718fbac674f06901b77bfcdce85b10e2a5d0ee91be5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.6.1"
"markers": "python_version >= '3.6'",
"version": "==5.0.9"
},
"aniso8601": {
"hashes": [
@ -89,7 +89,7 @@
"sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac",
"sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"
],
"markers": "python_version >= '3.6' and python_version < '3.9'",
"markers": "python_version < '3.9'",
"version": "==0.2.1"
},
"bcrypt": {
@ -131,11 +131,11 @@
},
"celery": {
"hashes": [
"sha256:a92e1d56e650781fb747032a3997d16236d037c8199eacd5217d1a72893bca45",
"sha256:d220b13a8ed57c78149acf82c006785356071844afe0b27012a4991d44026f9f"
"sha256:2844eb040e915398623a43253a8e1016723442ece6b0751a3c416d8a2b34216f",
"sha256:5a68a351076cfac4f678fa5ffd898105c28825a2224902da006970005196d061"
],
"index": "pypi",
"version": "==4.4.7"
"version": "==5.2.2"
},
"certifi": {
"hashes": [
@ -215,6 +215,28 @@
"markers": "python_version >= '3.6'",
"version": "==8.0.4"
},
"click-didyoumean": {
"hashes": [
"sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667",
"sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"
],
"markers": "python_version < '4' and python_full_version >= '3.6.2'",
"version": "==0.3.0"
},
"click-plugins": {
"hashes": [
"sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b",
"sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"
],
"version": "==1.1.1"
},
"click-repl": {
"hashes": [
"sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b",
"sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"
],
"version": "==0.2.0"
},
"clickclick": {
"hashes": [
"sha256:4efb13e62353e34c5eef7ed6582c4920b418d7dedc86d819e22ee089ba01802c",
@ -530,7 +552,7 @@
"sha256:175f4ee440a0317f6e8d81b7f8d4869f93316170a65ad2b007d2929186c8052c",
"sha256:e0bc84ff355328a4adfc5240c4f211e0ab386f80aa640d1b11f0618a1d282094"
],
"markers": "python_version < '3.10'",
"markers": "python_version < '3.9'",
"version": "==4.11.1"
},
"importlib-resources": {
@ -575,11 +597,11 @@
},
"kombu": {
"hashes": [
"sha256:be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a",
"sha256:ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74"
"sha256:81a90c1de97e08d3db37dbf163eaaf667445e1068c98bfd89f051a40e9f6dbbd",
"sha256:eeaeb8024f3a5cfc71c9250e45cddb8493f269d74ada2f74909a93c59c4b4179"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.6.11"
"markers": "python_version >= '3.7'",
"version": "==5.2.3"
},
"ldap3": {
"hashes": [
@ -813,6 +835,14 @@
"index": "pypi",
"version": "==1.4.1"
},
"prompt-toolkit": {
"hashes": [
"sha256:30129d870dcb0b3b6a53efdc9d0a83ea96162ffd28ffe077e94215b233dc670c",
"sha256:9f1cd16b1e86c2968f2519d7fb31dd9d669916f515612c269d14e9ed52b51650"
],
"markers": "python_full_version >= '3.6.2'",
"version": "==3.0.28"
},
"psycopg2-binary": {
"hashes": [
"sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7",
@ -1146,6 +1176,14 @@
"index": "pypi",
"version": "==0.14.4"
},
"setuptools": {
"hashes": [
"sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5",
"sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b"
],
"markers": "python_version >= '3.7'",
"version": "==60.9.3"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
@ -1313,11 +1351,11 @@
},
"vine": {
"hashes": [
"sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87",
"sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"
"sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
"sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.3.0"
"markers": "python_version >= '3.6'",
"version": "==5.0.0"
},
"waitress": {
"hashes": [
@ -1327,6 +1365,13 @@
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"wcwidth": {
"hashes": [
"sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
"sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
],
"version": "==0.2.5"
},
"webob": {
"hashes": [
"sha256:73aae30359291c14fa3b956f8b5ca31960e420c28c1bec002547fb04928cf89b",
@ -1437,7 +1482,7 @@
"sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
"sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
],
"markers": "python_version < '3.10'",
"markers": "python_version >= '3.7'",
"version": "==3.7.0"
}
},

View File

@ -244,9 +244,10 @@ def get_task_events(action = None, workflow = None, study = None):
study = session.query(StudyModel).filter(StudyModel.id == event.study_id).first()
workflow = session.query(WorkflowModel).filter(WorkflowModel.id == event.workflow_id).first()
spec = WorkflowSpecService().get_spec(workflow.workflow_spec_id)
workflow_meta = WorkflowMetadata.from_workflow(workflow, spec)
if study and study.status in [StudyStatus.open_for_enrollment, StudyStatus.in_progress]:
task_events.append(TaskEvent(event, study, workflow_meta))
if spec is not None:
workflow_meta = WorkflowMetadata.from_workflow(workflow, spec)
if study and study.status in [StudyStatus.open_for_enrollment, StudyStatus.in_progress]:
task_events.append(TaskEvent(event, study, workflow_meta))
return TaskEventSchema(many=True).dump(task_events)

View File

@ -0,0 +1,17 @@
from crc.api.common import ApiError
from crc.scripts.script import Script
class DeleteVariables(Script):
def get_description(self):
return """Script to delete variables from task_data, if they exist.
Accepts a list of variables to delete."""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
return self.do_task(task, study_id, workflow_id, *args, **kwargs)
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
for arg in args:
if arg in task.data:
del(task.data[arg])

View File

@ -4,7 +4,7 @@ from crc.scripts.script import Script
from crc.services.workflow_spec_service import WorkflowSpecService
class ScriptTemplate(Script):
class GetSpecFromID(Script):
def get_description(self):
return """Get workflow spec information from a workflow spec id"""

View File

@ -5,7 +5,7 @@ from crc.scripts.script import Script
from crc.services.workflow_spec_service import WorkflowSpecService
class ScriptTemplate(Script):
class GetSpecFromWorkflowID(Script):
def get_description(self):
return """Get a workflow spec, from a workflow id. You must pass in a workflow id."""

View File

@ -172,10 +172,9 @@ class WorkflowService(object):
"""Raise an exception of the workflow is not enabled and can not be executed."""
if study_id is not None:
study_model = session.query(StudyModel).filter(StudyModel.id == study_id).first()
spec_model = session.query(WorkflowSpecModel).filter(WorkflowSpecModel.id == spec_id).first()
status = StudyService._get_study_status(study_model)
if spec_model.id in status and status[spec_model.id]['status'] == 'disabled':
raise ApiError(code='disabled_workflow', message=f"This workflow is disabled. {status[spec_model.id]['message']}")
if spec_id in status and status[spec_id]['status'] == 'disabled':
raise ApiError(code='disabled_workflow', message=f"This workflow is disabled. {status[spec_id]['message']}")
@staticmethod
def test_spec(spec_id, validate_study_id=None, test_until=None, required_only=False):
@ -190,6 +189,7 @@ class WorkflowService(object):
"""
workflow_model = WorkflowService.make_test_workflow(spec_id, validate_study_id)
WorkflowService.raise_if_disabled(spec_id, validate_study_id)
try:
processor = WorkflowProcessor(workflow_model, validate_only=True)
count = 0

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0vysnuw" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
<bpmn:process id="Process_1so2rqu" name="Delete Variables from Task Data" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0zdl9n2</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0zdl9n2" sourceRef="StartEvent_1" targetRef="Activity_1ufzsi0" />
<bpmn:sequenceFlow id="Flow_14niiph" sourceRef="Activity_1ufzsi0" targetRef="Activity_16c0ane" />
<bpmn:sequenceFlow id="Flow_1wjlzq0" sourceRef="Activity_0nuz6z0" targetRef="Activity_0gtt0h2" />
<bpmn:endEvent id="Event_1dxgxez">
<bpmn:incoming>Flow_0aftk6v</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0aftk6v" sourceRef="Activity_0gtt0h2" targetRef="Event_1dxgxez" />
<bpmn:scriptTask id="Activity_1ufzsi0" name="Create Variables">
<bpmn:incoming>Flow_0zdl9n2</bpmn:incoming>
<bpmn:outgoing>Flow_14niiph</bpmn:outgoing>
<bpmn:script>a_item = 1
b_item = 2
c_item = 'a string'
d_item = a_item + b_item
e_item = datetime.datetime.now()</bpmn:script>
</bpmn:scriptTask>
<bpmn:scriptTask id="Activity_0nuz6z0" name="Delete Variables">
<bpmn:incoming>Flow_0ke44z0</bpmn:incoming>
<bpmn:outgoing>Flow_1wjlzq0</bpmn:outgoing>
<bpmn:script>delete_variables('a_item', 'b_item', 'c_item', 'd_item', 'e_item')</bpmn:script>
</bpmn:scriptTask>
<bpmn:manualTask id="Activity_0gtt0h2" name="Print Task Data">
<bpmn:documentation>## Task Data
{{ task_data }}</bpmn:documentation>
<bpmn:incoming>Flow_1wjlzq0</bpmn:incoming>
<bpmn:outgoing>Flow_0aftk6v</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:sequenceFlow id="Flow_0ke44z0" sourceRef="Activity_16c0ane" targetRef="Activity_0nuz6z0" />
<bpmn:manualTask id="Activity_16c0ane" name="Print Task Data">
<bpmn:documentation>## Task Data
{{ task_data }}</bpmn:documentation>
<bpmn:incoming>Flow_14niiph</bpmn:incoming>
<bpmn:outgoing>Flow_0ke44z0</bpmn:outgoing>
</bpmn:manualTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1so2rqu">
<bpmndi:BPMNEdge id="Flow_0ke44z0_di" bpmnElement="Flow_0ke44z0">
<di:waypoint x="530" y="117" />
<di:waypoint x="588" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0aftk6v_di" bpmnElement="Flow_0aftk6v">
<di:waypoint x="848" y="117" />
<di:waypoint x="910" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1wjlzq0_di" bpmnElement="Flow_1wjlzq0">
<di:waypoint x="688" y="117" />
<di:waypoint x="748" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_14niiph_di" bpmnElement="Flow_14niiph">
<di:waypoint x="370" y="117" />
<di:waypoint x="430" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0zdl9n2_di" bpmnElement="Flow_0zdl9n2">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1dxgxez_di" bpmnElement="Event_1dxgxez">
<dc:Bounds x="910" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ukmng8_di" bpmnElement="Activity_1ufzsi0">
<dc:Bounds x="270" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ykd81y_di" bpmnElement="Activity_0nuz6z0">
<dc:Bounds x="588" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1x2rbnn_di" bpmnElement="Activity_0gtt0h2">
<dc:Bounds x="748" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0baxotx_di" bpmnElement="Activity_16c0ane">
<dc:Bounds x="430" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -1,11 +1,10 @@
from tests.base_test import BaseTest
from crc import session
from crc.models.task_event import TaskEventModel
from crc.models.user import UserModel
from crc.services.user_service import UserService
from crc.services.workflow_service import WorkflowService
from example_data import ExampleDataLoader
from crc.services.workflow_spec_service import WorkflowSpecService
import json
@ -26,6 +25,60 @@ class TestWorkflowApi(BaseTest):
headers=self.logged_in_headers())
self.assert_success(rv)
def test_get_task_events_bad_spec(self):
self.add_studies()
workflow = self.create_workflow('hello_world')
# add a task_event
task_event = TaskEventModel(
study_id=workflow.study_id,
user_uid='dhf8r',
workflow_id=workflow.id,
workflow_spec_id=workflow.workflow_spec_id,
spec_version='',
action='',
task_id='',
task_name='my task name',
task_title='my task title',
task_state='',
task_lane='',
form_data='',
mi_type='',
mi_count=None,
mi_index=None,
process_name='',
date=None
)
session.add(task_event)
session.commit()
# make sure we have a task_event
tasks = session.query(TaskEventModel).filter(TaskEventModel.user_uid=='dhf8r').all()
self.assertEqual(1, len(tasks))
rv = self.app.get(f'/v1.0/task_events',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
data = json.loads(rv.get_data(as_text=True))
# make sure we get the task_event
self.assertEqual('my task title', data[0]['task_title'])
# delete the workflow spec
WorkflowSpecService().delete_spec('hello_world')
# try to get the task_event again
rv = self.app.get(f'/v1.0/task_events',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
# make sure we don't get an error, even though the spec no longer exists
self.assert_success(rv)
# make sure we don't get any events back.
self.assertEqual([], rv.json)
def test_library_code(self):
spec1 = self.load_test_spec('hello_world')

View File

@ -0,0 +1,19 @@
from tests.base_test import BaseTest
class TestDeleteVariables(BaseTest):
def test_delete_variables(self):
"""This workflow creates variables 'a', 'b', 'c', 'd', and 'e',
then deletes them with the delete_variables script.
We assert that they are no longer in task.data"""
workflow = self.create_workflow('delete_variables')
workflow_api = self.get_workflow_api(workflow)
task = workflow_api.next_task
items = ('a_item', 'b_item', 'c_item', 'd_item', 'e_item')
for item in items:
self.assertIn(item, task.data)
workflow_api = self.complete_form(workflow, task, {})
task = workflow_api.next_task
for item in items:
self.assertNotIn(item, task.data)