From 93b12a8e82659677953dbdfec38d553bd49149e9 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 4 Dec 2020 17:56:12 -0500 Subject: [PATCH] updates using new navigation from spiff workflow's navigation branch, all tests passing. --- Pipfile | 2 +- Pipfile.lock | 49 +- crc/api/workflow.py | 8 +- crc/models/api_models.py | 33 +- crc/services/workflow_service.py | 38 +- .../irb_api_personnel/irb_api_personnel.bpmn | 610 +++++++++++------- tests/test_tasks_api.py | 49 +- tests/test_user_roles.py | 56 +- .../test_workflow_processor_multi_instance.py | 2 +- 9 files changed, 491 insertions(+), 356 deletions(-) diff --git a/Pipfile b/Pipfile index 0a498025..e49b6613 100644 --- a/Pipfile +++ b/Pipfile @@ -39,7 +39,7 @@ requests = "*" sentry-sdk = {extras = ["flask"],version = "==0.14.4"} sphinx = "*" swagger-ui-bundle = "*" -spiffworkflow = {git = "https://github.com/sartography/SpiffWorkflow.git",ref = "master"} +spiffworkflow = {git = "https://github.com/sartography/SpiffWorkflow.git",ref = "bug/navigation"} webtest = "*" werkzeug = "*" xlrd = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ed6931eb..edda7a09 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "621d57ec513f24c665dd34e08ae5a19fc27784e87c55bb4d2a91a1d48a473081" + "sha256": "1e41c4486a74db3ee30b7fd4572b0cc54cfbe1bc1e6246b855748ec78db6cc1e" }, "pipfile-spec": 6, "requires": { @@ -33,10 +33,10 @@ }, "aniso8601": { "hashes": [ - "sha256:529dcb1f5f26ee0df6c0a1ee84b7b27197c3c50fc3a6321d66c544689237d072", - "sha256:c033f63d028b9a58e3ab0c2c7d0532ab4bfa7452bfc788fbfe3ddabd327b181a" + "sha256:246bf8d3611527030889e6df970878969d3a2f760ba3eb694fa1fb10e6ce53f9", + "sha256:51047d4fb51d7b8afd522b70f2d21a1b2487cbb7f7bd84ea852e9aa7808e7704" ], - "version": "==8.0.0" + "version": "==8.1.0" }, "attrs": { "hashes": [ @@ -108,12 +108,14 @@ "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35", "sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26", "sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b", + "sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01", "sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb", "sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293", "sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd", "sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d", "sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3", "sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d", + "sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e", "sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca", "sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d", "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775", @@ -548,10 +550,10 @@ }, "packaging": { "hashes": [ - "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", - "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" + "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236", + "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376" ], - "version": "==20.4" + "version": "==20.7" }, "pandas": { "hashes": [ @@ -640,11 +642,11 @@ }, "pygithub": { "hashes": [ - "sha256:776befaddab9d8fddd525d52a6ca1ac228cf62b5b1e271836d766f4925e1452e", - "sha256:8ad656bf79958e775ec59f7f5a3dbcbadac12147ae3dc42708b951064096af15" + "sha256:053f1b8d553a344ebd3ca3972765d923ee7e8ecc3ea55bd203683f164348fa1a", + "sha256:14c96d55e3c0e295598e52fbbbf2a7862a293723482ae9000cb9c816faab4fb4" ], "index": "pypi", - "version": "==1.53" + "version": "==1.54" }, "pygments": { "hashes": [ @@ -746,11 +748,11 @@ }, "requests": { "hashes": [ - "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", - "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" ], "index": "pypi", - "version": "==2.25.0" + "version": "==2.24.0" }, "sentry-sdk": { "extras": [ @@ -837,7 +839,7 @@ }, "spiffworkflow": { "git": "https://github.com/sartography/SpiffWorkflow.git", - "ref": "6b2ed24bb340ebd31049312bd321f66ebf7b6b26" + "ref": "cdab930848493d74250224f1956177fee231a5b7" }, "sqlalchemy": { "hashes": [ @@ -892,10 +894,10 @@ }, "urllib3": { "hashes": [ - "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", - "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" + "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", + "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" ], - "version": "==1.26.2" + "version": "==1.25.11" }, "waitress": { "hashes": [ @@ -1014,10 +1016,10 @@ }, "packaging": { "hashes": [ - "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", - "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" + "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236", + "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376" ], - "version": "==20.4" + "version": "==20.7" }, "pbr": { "hashes": [ @@ -1056,13 +1058,6 @@ "index": "pypi", "version": "==6.1.2" }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "version": "==1.15.0" - }, "toml": { "hashes": [ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", diff --git a/crc/api/workflow.py b/crc/api/workflow.py index a148328a..8462d6ec 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -1,13 +1,13 @@ import uuid -from SpiffWorkflow.util.deep_merge import DeepMerge from flask import g -from crc import session, app + +from crc import session from crc.api.common import ApiError, ApiErrorSchema -from crc.models.api_models import WorkflowApi, WorkflowApiSchema, NavigationItem, NavigationItemSchema +from crc.models.api_models import WorkflowApiSchema from crc.models.file import FileModel, LookupDataSchema from crc.models.study import StudyModel, WorkflowMetadata -from crc.models.task_event import TaskEventModel, TaskEventModelSchema, TaskEvent, TaskEventSchema +from crc.models.task_event import TaskEventModel, TaskEvent, TaskEventSchema from crc.models.workflow import WorkflowModel, WorkflowSpecModelSchema, WorkflowSpecModel, WorkflowSpecCategoryModel, \ WorkflowSpecCategoryModelSchema from crc.services.file_service import FileService diff --git a/crc/models/api_models.py b/crc/models/api_models.py index f526e4b2..6f2e08da 100644 --- a/crc/models/api_models.py +++ b/crc/models/api_models.py @@ -1,6 +1,7 @@ import enum import marshmallow +from SpiffWorkflow.navigation import NavItem from marshmallow import INCLUDE from marshmallow_enum import EnumField @@ -15,22 +16,6 @@ class MultiInstanceType(enum.Enum): sequential = "sequential" -class NavigationItem(object): - def __init__(self, id, task_id, name, title, backtracks, level, indent, child_count, state, is_decision, - task=None, lane=None): - self.id = id - self.task_id = task_id - self.name = name, - self.title = title - self.backtracks = backtracks - self.level = level - self.indent = indent - self.child_count = child_count - self.state = state - self.is_decision = is_decision - self.task = task - self.lane = lane - class Task(object): ########################################################################## @@ -158,15 +143,23 @@ class TaskSchema(ma.Schema): class NavigationItemSchema(ma.Schema): class Meta: - fields = ["id", "task_id", "name", "title", "backtracks", "level", "indent", "child_count", "state", - "is_decision", "task", "lane"] + fields = ["spec_id", "name", "spec_type", "task_id", "description", "backtracks", "indent", + "lane", "state"] unknown = INCLUDE - task = marshmallow.fields.Nested(TaskSchema, dump_only=True, required=False, allow_none=True) + state = marshmallow.fields.String(required=False, allow_none=True) + description = marshmallow.fields.String(required=False, allow_none=True) backtracks = marshmallow.fields.String(required=False, allow_none=True) lane = marshmallow.fields.String(required=False, allow_none=True) - title = marshmallow.fields.String(required=False, allow_none=True) task_id = marshmallow.fields.String(required=False, allow_none=True) + @marshmallow.post_load + def make_nav(self, data, **kwargs): + state = data.pop('state', None) + task_id = data.pop('task_id', None) + item = NavItem(**data) + item.state = state + item.task_id = task_id + return item class WorkflowApi(object): def __init__(self, id, status, next_task, navigation, diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 9808766c..44d0add9 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -1,9 +1,8 @@ -import copy -import json import string -import uuid from datetime import datetime import random +import string +from datetime import datetime from typing import List import jinja2 @@ -17,15 +16,14 @@ from SpiffWorkflow.bpmn.specs.UserTask import UserTask from SpiffWorkflow.dmn.specs.BusinessRuleTask import BusinessRuleTask from SpiffWorkflow.specs import CancelTask, StartTask from SpiffWorkflow.util.deep_merge import DeepMerge -from flask import g from jinja2 import Template from crc import db, app from crc.api.common import ApiError -from crc.models.api_models import Task, MultiInstanceType, NavigationItem, NavigationItemSchema, WorkflowApi +from crc.models.api_models import Task, MultiInstanceType, WorkflowApi from crc.models.file import LookupDataModel -from crc.models.task_event import TaskEventModel from crc.models.study import StudyModel +from crc.models.task_event import TaskEventModel from crc.models.user import UserModel, UserModelSchema from crc.models.workflow import WorkflowModel, WorkflowStatus, WorkflowSpecModel from crc.services.file_service import FileService @@ -326,28 +324,20 @@ class WorkflowService(object): # Some basic cleanup of the title for the for the navigation. navigation = [] for nav_item in nav_dict: - spiff_task = processor.bpmn_workflow.get_task(nav_item['task_id']) - if 'description' in nav_item: - nav_item['title'] = nav_item.pop('description') - # fixme: duplicate code from the workflow_service. Should only do this in one place. - if nav_item['title'] is not None and ' ' in nav_item['title']: - nav_item['title'] = nav_item['title'].partition(' ')[2] - else: - nav_item['title'] = "" + spiff_task = processor.bpmn_workflow.get_task(nav_item.task_id) if spiff_task: - nav_item['task'] = WorkflowService.spiff_task_to_api_task(spiff_task, add_docs_and_forms=False) - nav_item['title'] = nav_item['task'].title # Prefer the task title. - + # Use existing logic to set the description, and alter the state based on permissions. + api_task = WorkflowService.spiff_task_to_api_task(spiff_task, add_docs_and_forms=False) + nav_item.description = api_task.title user_uids = WorkflowService.get_users_assigned_to_task(processor, spiff_task) if not UserService.in_list(user_uids, allow_admin_impersonate=True): - nav_item['state'] = WorkflowService.TASK_STATE_LOCKED - + nav_item.state = WorkflowService.TASK_STATE_LOCKED else: - nav_item['task'] = None - - - navigation.append(NavigationItem(**nav_item)) - NavigationItemSchema().dump(nav_item) + # Strip off the first word in the description, to meet guidlines for BPMN. + if nav_item.description: + if nav_item.description is not None and ' ' in nav_item.description: + nav_item.description = nav_item.description.partition(' ')[2] + navigation.append(nav_item) spec = db.session.query(WorkflowSpecModel).filter_by(id=processor.workflow_spec_id).first() workflow_api = WorkflowApi( diff --git a/crc/static/bpmn/irb_api_personnel/irb_api_personnel.bpmn b/crc/static/bpmn/irb_api_personnel/irb_api_personnel.bpmn index 5beeba52..29262cef 100644 --- a/crc/static/bpmn/irb_api_personnel/irb_api_personnel.bpmn +++ b/crc/static/bpmn/irb_api_personnel/irb_api_personnel.bpmn @@ -1,11 +1,12 @@ - + Flow_0kcrx5l Flow_0kcrx5l + Flow_00zanzw Flow_1dcsioh current_user = ldap() investigators = study_info('investigators') @@ -15,11 +16,11 @@ is_cu_pi = False if pi != None: hasPI = True if pi.get('uid', None) != None: - pi_has_uid = True + pi_invalid_uid = False if pi['uid'] == current_user['uid']: is_cu_pi = True else: - pi_has_uid = False + pi_invalid_uid = True else: hasPI = False @@ -27,9 +28,11 @@ else: dc = investigators.get('DEPT_CH', None) if dc != None: if dc.get('uid', None) != None: - dc_has_uid = True + dc_invalid_uid = False else: - dc_has_uid = False + dc_invalid_uid = True +else: + dc_invalid_uid = False # Primary Coordinators pcs = {} @@ -39,13 +42,19 @@ for k in investigators.keys(): if k in ['SC_I','SC_II','IRBC']: investigator = investigators.get(k) if investigator.get('uid', None) != None: - cnt_pcs_uid = cnt_pcs_uid + 1 if investigator['uid'] != current_user['uid']: pcs[k] = investigator + cnt_pcs_uid = cnt_pcs_uid + 1 else: is_cu_pc = True is_cu_pc_role = investigator['label'] + else: + pcs[k] = investigator cnt_pcs = len(pcs.keys()) +if cnt_pcs != cnt_pcs_uid: + pcs_invalid_uid = True +else: + pcs_invalid_uid = False if cnt_pcs > 0: del(k) del(investigator) @@ -58,13 +67,19 @@ for k in investigators.keys(): if k == 'AS_C': investigator = investigators.get(k) if investigator.get('uid', None) != None: - cnt_acs_uid = cnt_acs_uid + 1 if investigator['uid'] != current_user['uid']: acs[k] = investigator + cnt_acs_uid = cnt_acs_uid + 1 else: is_cu_ac = True is_cu_ac_role = investigator['label'] + else: + acs[k] = investigator cnt_acs = len(acs.keys()) +if cnt_pcs != cnt_pcs_uid: + acs_invalid_uid = True +else: + acs_invalid_uid = False if cnt_acs > 0: del(k) del(investigator) @@ -77,12 +92,18 @@ for k in investigators.keys(): if k[:2] == 'SI': investigator = investigators.get(k) if investigator.get('uid', None) != None: - cnt_subs_uid = cnt_subs_uid + 1 if investigator['uid'] != current_user['uid']: subs[k] = investigator + cnt_subs_uid = cnt_subs_uid + 1 else: is_cu_subs = True + else: + subs[k] = investigator cnt_subs = len(subs.keys()) +if cnt_subs != cnt_subs_uid: + subs_invalid_uid = True +else: + subs_invalid_uid = False if cnt_subs > 0: del(k) del(investigator) @@ -95,13 +116,19 @@ for k in investigators.keys(): if k in ['SCI','DC']: investigator = investigators.get(k) if investigator.get('uid', None) != None: - cnt_aps_uid = cnt_aps_uid + 1 if investigator['uid'] != current_user['uid']: aps[k] = investigator + cnt_aps_uid = cnt_aps_uid + 1 else: is_cu_ap = True is_cu_ap_role = investigator['label'] + else: + aps[k] = investigator cnt_aps = len(aps.keys()) +if cnt_aps != cnt_aps_uid: + aps_invalid_uid = True +else: + aps_invalid_uid = False if cnt_aps > 0: del(k) del(investigator) @@ -132,12 +159,12 @@ Since you are the person entering this information, you already have access and - + - + @@ -160,23 +187,23 @@ Since you are the person entering this information, you already have access and - + Flow_1dcsioh Flow_147b9li Flow_00prawo - - - not(hasPI) or (hasPI and not(pi_has_uid)) + + + not(hasPI) or (hasPI and pi_invalid_uid) - + No PI entered in PB Flow_00prawo Flow_16qr5jf Flow_0kpe12r - SequenceFlow_0cdtt11 + Flow_1ayisx2 Flow_0xifvai Flow_1oqem42 @@ -210,7 +237,8 @@ Otherwise, edit each Coordinator as necessary and select the Save button for eac cnt_pcs == 0 - Flow_147b9li + Flow_0tfprc8 + Flow_0tsdclr Flow_1grahhv LDAP_dept = pi.department length_LDAP_dept = len(LDAP_dept) @@ -274,36 +302,12 @@ else: Flow_0w4d2bz Flow_1oo0ijr - - ***Name & Degree:*** {{ RO_Chair_Name_Degree }} -***School:*** {{ RO_School }} -***Department:*** {{ RO_Department }} -***Title:*** {{ RO_Chair_Title }} -***Email:*** {{ RO_Chair_CID }} - - -{% if RO_Chair_CID != dc.uid %} - *Does not match the Department Chair specified in Protocol Builder, {{ dc.display_name }}* -{% endif %} - - - - - - - - - - Flow_0vi6thu - SequenceFlow_0cdtt11 - - Flow_070j5fg Flow_0vi6thu Flow_00yhlrq - + RO_Chair_CID == pi.uid @@ -360,6 +364,7 @@ Otherwise, edit each Sub-Investigator as necessary and select the Save button fo Flow_1kg5jot pi.E0.schoolName = PI_E0_schoolName pi.E0.deptName = PI_E0_deptName +pi.experience = user_data_get("pi_experience","") ro = {} ro['chair'] = {} @@ -605,17 +610,17 @@ ro.schoolAbbrv = RO_StudySchool.value Flow_0vff9k5 - + Flow_0ofpgml Flow_0jxzqw1 Flow_0q56tn8 Flow_0kp47dz - - cnt_aps > 0 - + - + + cnt_aps == 0 + The following Additional Personnel were entered in Protocol Builder: {%+ for key, value in aps.items() %}{{value.display_name}} ({{key}}){% if loop.index is lt cnt_aps %}, {% endif %}{% endfor %} @@ -646,400 +651,545 @@ Otherwise, edit each Additional Personnel as necessary and select the Save butto Flow_10zn0h1 + + Flow_147b9li + Flow_0tfprc8 + Flow_0nz62mu + + + + dc_invalid_uid or pcs_invalid_uid or acs_invalid_uid or subs_invalid_uid or aps_invalid_uid + + + Select No if all displayed invalid Computing IDs do not need system access and/or receive emails. If they do, correct in Protocol Builder first and then select Yes. + + +{% if dc_invalid_uid %} +Department Chair + {{ dc.error }} +{% endif %} +{% if pcs_invalid_uid %} +Primary Coordinators +{% for k, pc in pcs.items() %} + {% if pc.get('uid', None) == None: %} + {{ pc.error }} + {% endif %} +{% endfor %} +{% endif %} +{% if acs_invalid_uid %} +Additional Coordinators +{% for k, ac in acs.items() %} + {% if ac.get('uid', None) == None: %} + {{ ac.error }} + {% endif %} +{% endfor %} +{% endif %} +{% if subs_invalid_uid %} +Sub-Investigators +{% for k, sub in subs.items() %} + {% if sub.get('uid', None) == None: %} + {{ sub.error }} + {% endif %} +{% endfor %} +{% endif %} +{% if aps_invalid_uid %} +Additional Personnnel +{% for k, ap in aps.items() %} + {% if ap.get('uid', None) == None: %} + {{ ap.error }} + {% endif %} +{% endfor %} +{% endif %} + + + + + + + + + + Flow_0nz62mu + Flow_16bkbuc + + + Flow_16bkbuc + Flow_00zanzw + Flow_0tsdclr + + + + + not(FixInvalidUIDs) + + + ***Name & Degree:*** {{ RO_Chair_Name_Degree }} +***School:*** {{ RO_School }} +***Department:*** {{ RO_Department }} +***Title:*** {{ RO_Chair_Title }} +***Email:*** {{ RO_Chair_CID }} + + +{% if RO_Chair_CID != dc.uid %} + *Does not match the Department Chair specified in Protocol Builder, {{ dc.display_name }}* +{% endif %} + + + + + + + + + + Flow_0vi6thu + Flow_1ayisx2 + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - + + - - + + - - + + - - - + + + - + - - - + + + - + - - + + - - - + + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - - - + + + + - + - - - + + + - - + + - - + + - - + + - + - - + + - + - - + + - + - - - - + + + + - + - - + + - - + + - + - - + + - - + + - - + + - - - - + + + + - + - - + + - - + + - + - - - + + + - - + + - + - - + + - + - - - - - - + + - - + + - - + + - - + + - - - - + + + + - + - - + + - - + + - + - - - + + + - + - - + + - + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_tasks_api.py b/tests/test_tasks_api.py index a6ffa710..e80c79a6 100644 --- a/tests/test_tasks_api.py +++ b/tests/test_tasks_api.py @@ -68,7 +68,7 @@ class TestTasksApi(BaseTest): # get the first form in the two form workflow. workflow_api = self.get_workflow_api(workflow) self.assertEqual('two_forms', workflow_api.workflow_spec_id) - self.assertEqual(2, len(workflow_api.navigation)) + self.assertEqual(4, len(workflow_api.navigation)) self.assertIsNotNone(workflow_api.next_task.form) self.assertEqual("UserTask", workflow_api.next_task.type) self.assertEqual("StepOne", workflow_api.next_task.name) @@ -113,14 +113,19 @@ class TestTasksApi(BaseTest): self.assertIsNotNone(workflow_api.navigation) nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual("Do You Have Bananas", nav[0]['title']) - self.assertEqual("Bananas?", nav[1]['title']) - self.assertEqual("FUTURE", nav[1]['state']) - self.assertEqual("yes", nav[2]['title']) - self.assertEqual("NOOP", nav[2]['state']) - self.assertEqual("no", nav[3]['title']) - self.assertEqual("NOOP", nav[3]['state']) + self.assertEqual(9, len(nav)) + self.assertEqual("Do You Have Bananas", nav[1].description) + self.assertEqual("Bananas?", nav[2].description) + self.assertEqual("FUTURE", nav[2].state) + self.assertEqual("yes", nav[3].description) + self.assertEqual(None, nav[3].state) + self.assertEqual("Task_Num_Bananas", nav[4].name) + self.assertEqual("LIKELY", nav[4].state) + self.assertEqual("EndEvent", nav[5].spec_type) + self.assertEqual("no", nav[6].description) + self.assertEqual(None, nav[6].state) + self.assertEqual("Task_Why_No_Bananas", nav[7].name) + self.assertEqual("MAYBE", nav[7].state) def test_navigation_with_exclusive_gateway(self): workflow = self.create_workflow('exclusive_gateway_2') @@ -129,14 +134,14 @@ class TestTasksApi(BaseTest): workflow_api = self.get_workflow_api(workflow) self.assertIsNotNone(workflow_api.navigation) nav = workflow_api.navigation - self.assertEqual(7, len(nav)) - self.assertEqual("Task 1", nav[0]['title']) - self.assertEqual("Which Branch?", nav[1]['title']) - self.assertEqual("a", nav[2]['title']) - self.assertEqual("Task 2a", nav[3]['title']) - self.assertEqual("b", nav[4]['title']) - self.assertEqual("Task 2b", nav[5]['title']) - self.assertEqual("Task 3", nav[6]['title']) + self.assertEqual(10, len(nav)) + self.assertEqual("Task 1", nav[1].description) + self.assertEqual("Which Branch?", nav[2].description) + self.assertEqual("a", nav[3].description) + self.assertEqual("Task 2a", nav[4].description) + self.assertEqual("b", nav[5].description) + self.assertEqual("Task 2b", nav[6].description) + self.assertEqual("Task 3", nav[8].description) def test_document_added_to_workflow_shows_up_in_file_list(self): self.create_reference_document() @@ -385,7 +390,7 @@ class TestTasksApi(BaseTest): navigation = workflow_api.navigation task = workflow_api.next_task - self.assertEqual(2, len(navigation)) + self.assertEqual(5, len(navigation)) self.assertEqual("UserTask", task.type) self.assertEqual("Activity_A", task.name) self.assertEqual("My Sub Process", task.process_name) @@ -453,7 +458,7 @@ class TestTasksApi(BaseTest): workflow_api = self.get_workflow_api(workflow) self.assertEqual(8, len(workflow_api.navigation)) - ready_items = [nav for nav in workflow_api.navigation if nav['state'] == "READY"] + ready_items = [nav for nav in workflow_api.navigation if nav.state == "READY"] self.assertEqual(5, len(ready_items)) self.assertEqual("UserTask", workflow_api.next_task.type) @@ -461,8 +466,8 @@ class TestTasksApi(BaseTest): self.assertEqual("Primary Investigator", workflow_api.next_task.title) for i in random.sample(range(5), 5): - task = TaskSchema().load(ready_items[i]['task']) - rv = self.app.put('/v1.0/workflow/%i/task/%s/set_token' % (workflow.id, task.id), + task_id = ready_items[i].task_id + rv = self.app.put('/v1.0/workflow/%i/task/%s/set_token' % (workflow.id, task_id), headers=self.logged_in_headers(), content_type="application/json") self.assert_success(rv) @@ -470,7 +475,7 @@ class TestTasksApi(BaseTest): workflow = WorkflowApiSchema().load(json_data) data = workflow.next_task.data data['investigator']['email'] = "dhf8r@virginia.edu" - self.complete_form(workflow, task, data) + self.complete_form(workflow, workflow.next_task, data) #tasks = self.get_workflow_api(workflow).user_tasks workflow = self.get_workflow_api(workflow) diff --git a/tests/test_user_roles.py b/tests/test_user_roles.py index 3dea94e7..90e452b0 100644 --- a/tests/test_user_roles.py +++ b/tests/test_user_roles.py @@ -1,6 +1,8 @@ import json from tests.base_test import BaseTest + +from crc.models.api_models import NavigationItemSchema from crc.models.workflow import WorkflowStatus from crc import db from crc.api.common import ApiError @@ -62,8 +64,8 @@ class TestTasksApi(BaseTest): workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid) nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual("supervisor", nav[1]['lane']) + self.assertEqual(9, len(nav)) + self.assertEqual("supervisor", nav[2].lane) def test_get_outstanding_tasks_awaiting_current_user(self): submitter = self.create_user(uid='lje5u') @@ -121,12 +123,12 @@ class TestTasksApi(BaseTest): # Navigation as Submitter with ready task. workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid) nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual('READY', nav[0]['state']) # First item is ready, no progress yet. - self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user. - self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway, and belongs to no one, and is locked. - self.assertEqual('NOOP', nav[3]['state']) # Approved Path, has no operation - self.assertEqual('NOOP', nav[4]['state']) # Rejected Path, has no operation. + self.assertEqual(9, len(nav)) + self.assertEqual('READY', nav[1].state) # First item is ready, no progress yet. + self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user. + # third item is a gateway, and belongs to no one + self.assertEqual(None, nav[4].state) # Approved Path, has no operation + self.assertEqual(None, nav[6].state) # Rejected Path, has no operation. self.assertEqual('READY', workflow_api.next_task.state) # Navigation as Submitter after handoff to supervisor @@ -134,9 +136,9 @@ class TestTasksApi(BaseTest): data['supervisor'] = supervisor.uid workflow_api = self.complete_form(workflow, workflow_api.next_task, data, user_uid=submitter.uid) nav = workflow_api.navigation - self.assertEqual('COMPLETED', nav[0]['state']) # First item is ready, no progress yet. - self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user. - self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway, and belongs to no one, and is locked. + self.assertEqual('COMPLETED', nav[1].state) # First item is ready, no progress yet. + self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user. + self.assertEqual('MAYBE', nav[7].state) # third item is a gateway, and belongs to no one, and is locked. self.assertEqual('LOCKED', workflow_api.next_task.state) # In the event the next task is locked, we should say something sensible here. # It is possible to look at the role of the task, and say The next task "TASK TITLE" will @@ -149,10 +151,10 @@ class TestTasksApi(BaseTest): # Navigation as Supervisor workflow_api = self.get_workflow_api(workflow, user_uid=supervisor.uid) nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual('LOCKED', nav[0]['state']) # First item belongs to the submitter, and is locked. - self.assertEqual('READY', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user. - self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway, and belongs to no one, and is locked. + self.assertEqual(9, len(nav)) + self.assertEqual('LOCKED', nav[1].state) # First item belongs to the submitter, and is locked. + self.assertEqual('READY', nav[2].state) # Second item is ready, as we are now the supervisor. + self.assertEqual('LOCKED', nav[7].state) # Feedback is locked. self.assertEqual('READY', workflow_api.next_task.state) data = workflow_api.next_task.data @@ -161,28 +163,28 @@ class TestTasksApi(BaseTest): # Navigation as Supervisor, after completing task. nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual('LOCKED', nav[0]['state']) # First item belongs to the submitter, and is locked. - self.assertEqual('COMPLETED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user. - self.assertEqual('COMPLETED', nav[2]['state']) # third item is a gateway, and is now complete. + self.assertEqual(9, len(nav)) + self.assertEqual('LOCKED', nav[1].state) # First item belongs to the submitter, and is locked. + self.assertEqual('COMPLETED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user. + self.assertEqual('LOCKED', nav[7].state) # Feedback is LOCKED self.assertEqual('LOCKED', workflow_api.next_task.state) # Navigation as Submitter, coming back in to a rejected workflow to view the rejection message. workflow_api = self.get_workflow_api(workflow, user_uid=submitter.uid) nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual('COMPLETED', nav[0]['state']) # First item belongs to the submitter, and is locked. - self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user. - self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway belonging to the supervisor, and is locked. + self.assertEqual(9, len(nav)) + self.assertEqual('COMPLETED', nav[1].state) # First item belongs to the submitter, and is locked. + self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user. + self.assertEqual('READY', nav[7].state) # Feedbck is now READY self.assertEqual('READY', workflow_api.next_task.state) # Navigation as Submitter, re-completing the original request a second time, and sending it for review. workflow_api = self.complete_form(workflow, workflow_api.next_task, data, user_uid=submitter.uid) nav = workflow_api.navigation - self.assertEqual(5, len(nav)) - self.assertEqual('READY', nav[0]['state']) # When you loop back the task is again in the ready state. - self.assertEqual('LOCKED', nav[1]['state']) # Second item is locked, it is the review and doesn't belong to this user. - self.assertEqual('LOCKED', nav[2]['state']) # third item is a gateway belonging to the supervisor, and is locked. + self.assertEqual(9, len(nav)) + self.assertEqual('READY', nav[1].state) # When you loop back the task is again in the ready state. + self.assertEqual('LOCKED', nav[2].state) # Second item is locked, it is the review and doesn't belong to this user. + self.assertEqual('COMPLETED', nav[7].state) # Feedback is completed self.assertEqual('READY', workflow_api.next_task.state) data["favorite_color"] = "blue" diff --git a/tests/workflow/test_workflow_processor_multi_instance.py b/tests/workflow/test_workflow_processor_multi_instance.py index 6fccb091..99339101 100644 --- a/tests/workflow/test_workflow_processor_multi_instance.py +++ b/tests/workflow/test_workflow_processor_multi_instance.py @@ -171,7 +171,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest): # Assure navigation picks up the label of the current element variable. nav = WorkflowService.processor_to_workflow_api(processor, task).navigation - self.assertEqual("Primary Investigator", nav[2].title) + self.assertEqual("Primary Investigator", nav[2].description) task.update_data({"investigator": {"email": "dhf8r@virginia.edu"}}) processor.complete_task(task)