From 860c475b29120497226bc1dc2f271e8dcb35c337 Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Sat, 30 May 2020 15:37:04 -0400 Subject: [PATCH 01/18] Fill out repeating sections during validation process. Also, when returning error messages, attempt to include the task data for the task that caused the error. Also, when attempting to delete any file, respond with an API error explaining the issue, and log the details. --- crc/api/common.py | 7 +- crc/models/api_models.py | 1 + crc/services/file_service.py | 27 +++-- crc/services/study_service.py | 3 + crc/services/workflow_service.py | 117 +++++++++++++-------- tests/data/repeat_form/repeat_form.bpmn | 47 +++++++++ tests/test_workflow_spec_validation_api.py | 16 ++- 7 files changed, 159 insertions(+), 59 deletions(-) create mode 100644 tests/data/repeat_form/repeat_form.bpmn diff --git a/crc/api/common.py b/crc/api/common.py index 2cd09522..b89dd8d5 100644 --- a/crc/api/common.py +++ b/crc/api/common.py @@ -3,7 +3,7 @@ from crc import ma, app class ApiError(Exception): def __init__(self, code, message, status_code=400, - file_name="", task_id="", task_name="", tag=""): + file_name="", task_id="", task_name="", tag="", task_data = {}): self.status_code = status_code self.code = code # a short consistent string describing the error. self.message = message # A detailed message that provides more information. @@ -11,6 +11,7 @@ class ApiError(Exception): self.task_name = task_name or "" # OPTIONAL: The name of the task in the BPMN Diagram. self.file_name = file_name or "" # OPTIONAL: The file that caused the error. self.tag = tag or "" # OPTIONAL: The XML Tag that caused the issue. + self.task_data = task_data or "" # OPTIONAL: A snapshot of data connected to the task when error ocurred. Exception.__init__(self, self.message) @classmethod @@ -20,6 +21,7 @@ class ApiError(Exception): instance.task_id = task.task_spec.name or "" instance.task_name = task.task_spec.description or "" instance.file_name = task.workflow.spec.file or "" + instance.task_data = task.data return instance @classmethod @@ -35,7 +37,8 @@ class ApiError(Exception): class ApiErrorSchema(ma.Schema): class Meta: - fields = ("code", "message", "workflow_name", "file_name", "task_name", "task_id") + fields = ("code", "message", "workflow_name", "file_name", "task_name", "task_id", + "task_data") @app.errorhandler(ApiError) diff --git a/crc/models/api_models.py b/crc/models/api_models.py index 4b279965..d53e43bc 100644 --- a/crc/models/api_models.py +++ b/crc/models/api_models.py @@ -31,6 +31,7 @@ class NavigationItem(object): class Task(object): + PROP_OPTIONS_REPEAT = "repeat" PROP_OPTIONS_FILE = "spreadsheet.name" PROP_OPTIONS_VALUE_COLUMN = "spreadsheet.value.column" PROP_OPTIONS_LABEL_COL = "spreadsheet.label.column" diff --git a/crc/services/file_service.py b/crc/services/file_service.py index beb22831..273460c1 100644 --- a/crc/services/file_service.py +++ b/crc/services/file_service.py @@ -5,11 +5,13 @@ from datetime import datetime from uuid import UUID from xml.etree import ElementTree +import flask from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException from pandas import ExcelFile from sqlalchemy import desc +from sqlalchemy.exc import IntegrityError -from crc import session +from crc import session, app from crc.api.common import ApiError from crc.models.file import FileType, FileDataModel, FileModel, LookupFileModel, LookupDataModel from crc.models.workflow import WorkflowSpecModel, WorkflowModel, WorkflowSpecDependencyFile @@ -295,12 +297,17 @@ class FileService(object): @staticmethod def delete_file(file_id): - data_models = session.query(FileDataModel).filter_by(file_model_id=file_id).all() - for dm in data_models: - lookup_files = session.query(LookupFileModel).filter_by(file_data_model_id=dm.id).all() - for lf in lookup_files: - session.query(LookupDataModel).filter_by(lookup_file_model_id=lf.id).delete() - session.query(LookupFileModel).filter_by(id=lf.id).delete() - session.query(FileDataModel).filter_by(file_model_id=file_id).delete() - session.query(FileModel).filter_by(id=file_id).delete() - session.commit() + try: + data_models = session.query(FileDataModel).filter_by(file_model_id=file_id).all() + for dm in data_models: + lookup_files = session.query(LookupFileModel).filter_by(file_data_model_id=dm.id).all() + for lf in lookup_files: + session.query(LookupDataModel).filter_by(lookup_file_model_id=lf.id).delete() + session.query(LookupFileModel).filter_by(id=lf.id).delete() + session.query(FileDataModel).filter_by(file_model_id=file_id).delete() + session.query(FileModel).filter_by(id=file_id).delete() + session.commit() + except IntegrityError as ie: + app.logger.error("Failed to delete file: %i, due to %s" % (file_id, str(ie))) + raise ApiError('file_integrity_error', "You are attempting to delete a file that is " + "required by other records in the system.") \ No newline at end of file diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 98a8d15a..424a911f 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -4,6 +4,7 @@ from typing import List import requests from SpiffWorkflow import WorkflowException +from SpiffWorkflow.exceptions import WorkflowTaskExecException from ldap3.core.exceptions import LDAPSocketOpenError from crc import db, session, app @@ -309,6 +310,8 @@ class StudyService(object): for workflow_spec in new_specs: try: StudyService._create_workflow_model(study_model, workflow_spec) + except WorkflowTaskExecException as wtee: + errors.append(ApiError.from_task("workflow_execution_exception", str(wtee), wtee.task)) except WorkflowException as we: errors.append(ApiError.from_task_spec("workflow_execution_exception", str(we), we.sender)) return errors diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index c6cb8638..cf40b84d 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -7,8 +7,9 @@ from SpiffWorkflow import Task as SpiffTask, WorkflowException from SpiffWorkflow.bpmn.specs.ManualTask import ManualTask from SpiffWorkflow.bpmn.specs.ScriptTask import ScriptTask from SpiffWorkflow.bpmn.specs.UserTask import UserTask -from SpiffWorkflow.bpmn.workflow import BpmnWorkflow +from SpiffWorkflow.camunda.specs.UserTask import EnumFormField from SpiffWorkflow.dmn.specs.BusinessRuleTask import BusinessRuleTask +from SpiffWorkflow.exceptions import WorkflowTaskExecException from SpiffWorkflow.specs import CancelTask, StartTask from flask import g from jinja2 import Template @@ -17,7 +18,6 @@ from crc import db, app from crc.api.common import ApiError from crc.models.api_models import Task, MultiInstanceType from crc.models.file import LookupDataModel -from crc.models.protocol_builder import ProtocolBuilderStatus from crc.models.stats import TaskEventModel from crc.models.study import StudyModel from crc.models.user import UserModel @@ -39,7 +39,9 @@ class WorkflowService(object): the workflow Processor should be hidden behind this service. This will help maintain a structure that avoids circular dependencies. But for now, this contains tools for converting spiff-workflow models into our - own API models with additional information and capabilities.""" + own API models with additional information and capabilities and + handles the testing of a workflow specification by completing it with + random selections, attempting to mimic a front end as much as possible. """ @staticmethod def make_test_workflow(spec_id): @@ -61,17 +63,25 @@ class WorkflowService(object): for study in db.session.query(StudyModel).filter(StudyModel.user_uid=="test"): StudyService.delete_study(study.id) db.session.commit() - db.session.query(UserModel).filter_by(uid="test").delete() + + user = db.session.query(UserModel).filter_by(uid="test").first() + if user: + db.session.delete(user) @staticmethod def test_spec(spec_id): - """Runs a spec through it's paces to see if it results in any errors. Not fool-proof, but a good - sanity check.""" + """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. """ workflow_model = WorkflowService.make_test_workflow(spec_id) try: processor = WorkflowProcessor(workflow_model, validate_only=True) + except WorkflowTaskExecException as wtee: + WorkflowService.delete_test_data() + raise ApiError.from_task("workflow_execution_exception", str(wtee), + wtee.task) except WorkflowException as we: WorkflowService.delete_test_data() raise ApiError.from_task_spec("workflow_execution_exception", str(we), @@ -87,11 +97,17 @@ class WorkflowService(object): add_docs_and_forms=True) # Assure we try to process the documenation, and raise those errors. WorkflowService.populate_form_with_random_data(task, task_api) task.complete() + except WorkflowTaskExecException as wtee: + WorkflowService.delete_test_data() + raise ApiError.from_task("workflow_execution_exception", str(wtee), + wtee.task) except WorkflowException as we: WorkflowService.delete_test_data() raise ApiError.from_task_spec("workflow_execution_exception", str(we), we.sender) + WorkflowService.delete_test_data() + return processor.bpmn_workflow.last_task.data @staticmethod def populate_form_with_random_data(task, task_api): @@ -101,22 +117,35 @@ class WorkflowService(object): form_data = {} for field in task_api.form.fields: - if field.type == "enum": - if len(field.options) > 0: - random_choice = random.choice(field.options) - if isinstance(random_choice, dict): - form_data[field.id] = random.choice(field.options)['id'] - else: - # fixme: why it is sometimes an EnumFormFieldOption, and other times not? - form_data[field.id] = random_choice.id ## Assume it is an EnumFormFieldOption + if field.has_property(Task.PROP_OPTIONS_REPEAT): + group = field.get_property(Task.PROP_OPTIONS_REPEAT) + if group not in form_data: + form_data[group] = [{},{},{}] + for i in range(3): + form_data[group][i][field.id] = WorkflowService.get_random_data_for_field(field, task) + else: + form_data[field.id] = WorkflowService.get_random_data_for_field(field, task) + if task.data is None: + task.data = {} + task.data.update(form_data) + + @staticmethod + def get_random_data_for_field(field, task): + if field.type == "enum": + if len(field.options) > 0: + random_choice = random.choice(field.options) + if isinstance(random_choice, dict): + return random.choice(field.options)['id'] else: - raise ApiError.from_task("invalid_enum", "You specified an enumeration field (%s)," - " with no options" % field.id, - task) - elif field.type == "autocomplete": - lookup_model = LookupService.get_lookup_model(task, field) - if field.has_property(Task.PROP_LDAP_LOOKUP): - form_data[field.id] = { + # fixme: why it is sometimes an EnumFormFieldOption, and other times not? + return random_choice.id ## Assume it is an EnumFormFieldOption + else: + raise ApiError.from_task("invalid_enum", "You specified an enumeration field (%s)," + " with no options" % field.id, task) + elif field.type == "autocomplete": + lookup_model = LookupService.get_lookup_model(task, field) + if field.has_property(Task.PROP_LDAP_LOOKUP): # All ldap records get the same person. + return { "label": "dhf8r", "value": "Dan Funk", "data": { @@ -126,32 +155,30 @@ class WorkflowService(object): "email_address": "dhf8r@virginia.edu", "department": "Depertment of Psychocosmographictology", "affiliation": "Rousabout", - "sponsor_type": "Staff" + "sponsor_type": "Staff"} } - } - elif lookup_model: - data = db.session.query(LookupDataModel).filter( - LookupDataModel.lookup_file_model == lookup_model).limit(10).all() - options = [] - for d in data: - options.append({"id": d.value, "name": d.label}) - form_data[field.id] = random.choice(options) - else: - raise ApiError.from_task("invalid_autocomplete", "The settings for this auto complete field " - "are incorrect: %s " % field.id, task) - elif field.type == "long": - form_data[field.id] = random.randint(1, 1000) - elif field.type == 'boolean': - form_data[field.id] = random.choice([True, False]) - elif field.type == 'file': - form_data[field.id] = random.randint(1, 100) - elif field.type == 'files': - form_data[field.id] = random.randrange(1, 100) + elif lookup_model: + data = db.session.query(LookupDataModel).filter( + LookupDataModel.lookup_file_model == lookup_model).limit(10).all() + options = [] + for d in data: + options.append({"id": d.value, "name": d.label}) + return random.choice(options) else: - form_data[field.id] = WorkflowService._random_string() - if task.data is None: - task.data = {} - task.data.update(form_data) + raise ApiError.from_task("invalid_autocomplete", "The settings for this auto complete field " + "are incorrect: %s " % field.id, task) + elif field.type == "long": + return random.randint(1, 1000) + elif field.type == 'boolean': + return random.choice([True, False]) + elif field.type == 'file': + # fixme: produce some something sensible for files. + return random.randint(1, 100) + # fixme: produce some something sensible for files. + elif field.type == 'files': + return random.randrange(1, 100) + else: + return WorkflowService._random_string() def __get_options(self): pass diff --git a/tests/data/repeat_form/repeat_form.bpmn b/tests/data/repeat_form/repeat_form.bpmn new file mode 100644 index 00000000..f0e3f922 --- /dev/null +++ b/tests/data/repeat_form/repeat_form.bpmn @@ -0,0 +1,47 @@ + + + + + SequenceFlow_0lvudp8 + + + + SequenceFlow_02vev7n + + + + + + + + + + + + + SequenceFlow_0lvudp8 + SequenceFlow_02vev7n + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_workflow_spec_validation_api.py b/tests/test_workflow_spec_validation_api.py index 9e581874..d46746dc 100644 --- a/tests/test_workflow_spec_validation_api.py +++ b/tests/test_workflow_spec_validation_api.py @@ -3,17 +3,16 @@ from unittest.mock import patch from tests.base_test import BaseTest -from crc.services.protocol_builder import ProtocolBuilderService from crc import session, app from crc.api.common import ApiErrorSchema from crc.models.protocol_builder import ProtocolBuilderStudySchema from crc.models.workflow import WorkflowSpecModel +from crc.services.workflow_service import WorkflowService class TestWorkflowSpecValidation(BaseTest): def validate_workflow(self, workflow_name): - self.load_example_data() spec_model = self.load_test_spec(workflow_name) rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) self.assert_success(rv) @@ -22,6 +21,7 @@ class TestWorkflowSpecValidation(BaseTest): def test_successful_validation_of_test_workflows(self): app.config['PB_ENABLED'] = False # Assure this is disabled. + self.load_example_data() self.assertEqual(0, len(self.validate_workflow("parallel_tasks"))) self.assertEqual(0, len(self.validate_workflow("decision_table"))) self.assertEqual(0, len(self.validate_workflow("docx"))) @@ -60,6 +60,7 @@ class TestWorkflowSpecValidation(BaseTest): self.assertEqual(0, len(errors), json.dumps(errors)) def test_invalid_expression(self): + self.load_example_data() errors = self.validate_workflow("invalid_expression") self.assertEqual(1, len(errors)) self.assertEqual("workflow_execution_exception", errors[0]['code']) @@ -68,8 +69,11 @@ class TestWorkflowSpecValidation(BaseTest): self.assertEqual("invalid_expression.bpmn", errors[0]['file_name']) self.assertEqual('ExclusiveGateway_003amsm: Error evaluating expression \'this_value_does_not_exist==true\', ' 'name \'this_value_does_not_exist\' is not defined', errors[0]["message"]) + self.assertIsNotNone(errors[0]['task_data']) + self.assertIn("has_bananas", errors[0]['task_data']) def test_validation_error(self): + self.load_example_data() errors = self.validate_workflow("invalid_spec") self.assertEqual(1, len(errors)) self.assertEqual("workflow_validation_error", errors[0]['code']) @@ -77,6 +81,7 @@ class TestWorkflowSpecValidation(BaseTest): self.assertEqual("invalid_spec.bpmn", errors[0]['file_name']) def test_invalid_script(self): + self.load_example_data() errors = self.validate_workflow("invalid_script") self.assertEqual(1, len(errors)) self.assertEqual("workflow_execution_exception", errors[0]['code']) @@ -84,3 +89,10 @@ class TestWorkflowSpecValidation(BaseTest): self.assertEqual("Invalid_Script_Task", errors[0]['task_id']) self.assertEqual("An Invalid Script Reference", errors[0]['task_name']) self.assertEqual("invalid_script.bpmn", errors[0]['file_name']) + + def test_repeating_sections_correctly_populated(self): + self.load_example_data() + spec_model = self.load_test_spec('repeat_form') + final_data = WorkflowService.test_spec(spec_model.id) + self.assertIsNotNone(final_data) + self.assertIn('cats', final_data) \ No newline at end of file From 1f0e8741ba228e0d71581097d888c41f79058b67 Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Sat, 30 May 2020 17:21:57 -0400 Subject: [PATCH 02/18] Run the validation twice, once completing all of the data, and a second time, completing only the required fields. Also, add a helper method to reduce boiler plate code around Workflow Exceptions. --- Pipfile.lock | 20 ++++---- crc/api/common.py | 13 +++++ crc/api/workflow.py | 2 + crc/models/api_models.py | 1 + crc/services/workflow_service.py | 31 +++++------- tests/data/decision_table/decision_table.bpmn | 37 +++++++------- .../exclusive_gateway/exclusive_gateway.bpmn | 6 ++- tests/data/random_fact/random_fact.bpmn | 44 +++++++++-------- .../data/required_fields/required_fields.bpmn | 48 +++++++++++++++++++ tests/test_workflow_processor.py | 2 +- tests/test_workflow_service.py | 2 +- tests/test_workflow_spec_validation_api.py | 15 +++++- 12 files changed, 151 insertions(+), 70 deletions(-) create mode 100644 tests/data/required_fields/required_fields.bpmn diff --git a/Pipfile.lock b/Pipfile.lock index d9c2bfab..ce620efc 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -428,10 +428,10 @@ }, "mako": { "hashes": [ - "sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d", - "sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9" + "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27", + "sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9" ], - "version": "==1.1.2" + "version": "==1.1.3" }, "markupsafe": { "hashes": [ @@ -489,11 +489,11 @@ }, "marshmallow-sqlalchemy": { "hashes": [ - "sha256:3247e41e424146340b03a369f2b7c6f0364477ccedc4e2481e84d5f3a8d3c67f", - "sha256:dbbe51d28bb28e7ee2782e51310477f7a2c5a111a301f6dd8e264e11ab820427" + "sha256:03a555b610bb307689b821b64e2416593ec21a85925c8c436c2cd08ebc6bb85e", + "sha256:0ef59c8da8da2e18e808e3880158049e9d72f3031c84cc804b6c533a0eb668a9" ], "index": "pypi", - "version": "==0.23.0" + "version": "==0.23.1" }, "numpy": { "hashes": [ @@ -778,7 +778,7 @@ "spiffworkflow": { "editable": true, "git": "https://github.com/sartography/SpiffWorkflow.git", - "ref": "c8d87826d496af825a184bdc3f0a751e603cfe44" + "ref": "b8a064a0bb76c705a1be04ee9bb8ac7beee56eb0" }, "sqlalchemy": { "hashes": [ @@ -876,11 +876,11 @@ }, "xlsxwriter": { "hashes": [ - "sha256:488e1988ab16ff3a9cd58c7656d0a58f8abe46ee58b98eecea78c022db28656b", - "sha256:97ab487b81534415c5313154203f3e8a637d792b1e6a8201e8f7f71da0203c2a" + "sha256:828b3285fc95105f5b1946a6a015b31cf388bd5378fdc6604e4d1b7839df2e77", + "sha256:82a3b0e73e3913483da23791d1a25e4d2dbb3837d1be4129473526b9a270a5cc" ], "index": "pypi", - "version": "==1.2.8" + "version": "==1.2.9" }, "zipp": { "hashes": [ diff --git a/crc/api/common.py b/crc/api/common.py index b89dd8d5..f8673a5b 100644 --- a/crc/api/common.py +++ b/crc/api/common.py @@ -1,3 +1,6 @@ +from SpiffWorkflow import WorkflowException +from SpiffWorkflow.exceptions import WorkflowTaskExecException + from crc import ma, app @@ -34,6 +37,16 @@ class ApiError(Exception): instance.file_name = task_spec._wf_spec.file return instance + @classmethod + def from_workflow_exception(cls, code, message, exp: WorkflowException): + """We catch a lot of workflow exception errors, + so consolidating the code, and doing the best things + we can with the data we have.""" + if isinstance(exp, WorkflowTaskExecException): + return ApiError.from_task(code, message, exp.task) + else: + return ApiError.from_task_spec(code, message, exp.sender) + class ApiErrorSchema(ma.Schema): class Meta: diff --git a/crc/api/workflow.py b/crc/api/workflow.py index efcccc26..9d6e4680 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -42,7 +42,9 @@ def validate_workflow_specification(spec_id): errors = [] try: + # Run the validation twice, the second time, just populate the required fields. WorkflowService.test_spec(spec_id) + WorkflowService.test_spec(spec_id, required_only=True) except ApiError as ae: errors.append(ae) return ApiErrorSchema(many=True).dump(errors) diff --git a/crc/models/api_models.py b/crc/models/api_models.py index d53e43bc..eee6d5f5 100644 --- a/crc/models/api_models.py +++ b/crc/models/api_models.py @@ -36,6 +36,7 @@ class Task(object): PROP_OPTIONS_VALUE_COLUMN = "spreadsheet.value.column" PROP_OPTIONS_LABEL_COL = "spreadsheet.label.column" PROP_LDAP_LOOKUP = "ldap.lookup" + VALIDATION_REQUIRED = "required" FIELD_TYPE_AUTO_COMPLETE = "autocomplete" diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index cf40b84d..dc900400 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -7,9 +7,7 @@ from SpiffWorkflow import Task as SpiffTask, WorkflowException from SpiffWorkflow.bpmn.specs.ManualTask import ManualTask from SpiffWorkflow.bpmn.specs.ScriptTask import ScriptTask from SpiffWorkflow.bpmn.specs.UserTask import UserTask -from SpiffWorkflow.camunda.specs.UserTask import EnumFormField from SpiffWorkflow.dmn.specs.BusinessRuleTask import BusinessRuleTask -from SpiffWorkflow.exceptions import WorkflowTaskExecException from SpiffWorkflow.specs import CancelTask, StartTask from flask import g from jinja2 import Template @@ -69,23 +67,22 @@ class WorkflowService(object): db.session.delete(user) @staticmethod - def test_spec(spec_id): + def test_spec(spec_id, 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. """ + output form the last task if successful. + + required_only can be set to true, in which case this will run the + spec, only completing the required fields, rather than everything. + """ workflow_model = WorkflowService.make_test_workflow(spec_id) try: processor = WorkflowProcessor(workflow_model, validate_only=True) - except WorkflowTaskExecException as wtee: - WorkflowService.delete_test_data() - raise ApiError.from_task("workflow_execution_exception", str(wtee), - wtee.task) except WorkflowException as we: WorkflowService.delete_test_data() - raise ApiError.from_task_spec("workflow_execution_exception", str(we), - we.sender) + raise ApiError.from_workflow_exception("workflow_execution_exception", str(we), we) while not processor.bpmn_workflow.is_completed(): try: @@ -95,28 +92,26 @@ class WorkflowService(object): task_api = WorkflowService.spiff_task_to_api_task( task, add_docs_and_forms=True) # Assure we try to process the documenation, and raise those errors. - WorkflowService.populate_form_with_random_data(task, task_api) + WorkflowService.populate_form_with_random_data(task, task_api, required_only) task.complete() - except WorkflowTaskExecException as wtee: - WorkflowService.delete_test_data() - raise ApiError.from_task("workflow_execution_exception", str(wtee), - wtee.task) except WorkflowException as we: WorkflowService.delete_test_data() - raise ApiError.from_task_spec("workflow_execution_exception", str(we), - we.sender) + raise ApiError.from_workflow_exception("workflow_execution_exception", str(we), we) WorkflowService.delete_test_data() return processor.bpmn_workflow.last_task.data @staticmethod - def populate_form_with_random_data(task, task_api): + def populate_form_with_random_data(task, task_api, required_only): """populates a task with random data - useful for testing a spec.""" if not hasattr(task.task_spec, 'form'): return form_data = {} for field in task_api.form.fields: + if required_only and (not field.has_validation(Task.VALIDATION_REQUIRED) or + field.get_validation(Task.VALIDATION_REQUIRED).lower().strip() != "true"): + continue # Don't include any fields that aren't specifically marked as required. if field.has_property(Task.PROP_OPTIONS_REPEAT): group = field.get_property(Task.PROP_OPTIONS_REPEAT) if group not in form_data: diff --git a/tests/data/decision_table/decision_table.bpmn b/tests/data/decision_table/decision_table.bpmn index 796233e5..82bcb385 100644 --- a/tests/data/decision_table/decision_table.bpmn +++ b/tests/data/decision_table/decision_table.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_1ma1wxb @@ -8,7 +8,11 @@ - + + + + + SequenceFlow_1ma1wxb @@ -26,38 +30,37 @@ Based on the information you provided (Ginger left {{num_presents}}, we recommen ## {{message}} -We hope you both have an excellent day! - +We hope you both have an excellent day! SequenceFlow_0grui6f - - - - - - + + + - - - + + + + + + + + + + - - - - diff --git a/tests/data/exclusive_gateway/exclusive_gateway.bpmn b/tests/data/exclusive_gateway/exclusive_gateway.bpmn index 1c7e55fe..8467c954 100644 --- a/tests/data/exclusive_gateway/exclusive_gateway.bpmn +++ b/tests/data/exclusive_gateway/exclusive_gateway.bpmn @@ -8,7 +8,11 @@ - + + + + + SequenceFlow_1pnq3kg diff --git a/tests/data/random_fact/random_fact.bpmn b/tests/data/random_fact/random_fact.bpmn index 81f355c3..628f1bd4 100644 --- a/tests/data/random_fact/random_fact.bpmn +++ b/tests/data/random_fact/random_fact.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_0c7wlth @@ -108,6 +108,9 @@ Autoconverted link https://github.com/nodeca/pica (enable linkify to see) + + + @@ -121,8 +124,7 @@ Autoconverted link https://github.com/nodeca/pica (enable linkify to see) SequenceFlow_0641sh6 - - + @@ -155,6 +157,18 @@ Your random fact is: + + + + + + + + + + + + @@ -164,35 +178,23 @@ Your random fact is: + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/tests/data/required_fields/required_fields.bpmn b/tests/data/required_fields/required_fields.bpmn new file mode 100644 index 00000000..7612f69b --- /dev/null +++ b/tests/data/required_fields/required_fields.bpmn @@ -0,0 +1,48 @@ + + + + + SequenceFlow_0lvudp8 + + + + SequenceFlow_02vev7n + + + + + + + + + + + + + + SequenceFlow_0lvudp8 + SequenceFlow_02vev7n + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_workflow_processor.py b/tests/test_workflow_processor.py index 36d23755..b3f6c374 100644 --- a/tests/test_workflow_processor.py +++ b/tests/test_workflow_processor.py @@ -25,7 +25,7 @@ class TestWorkflowProcessor(BaseTest): def _populate_form_with_random_data(self, task): api_task = WorkflowService.spiff_task_to_api_task(task, add_docs_and_forms=True) - WorkflowService.populate_form_with_random_data(task, api_task) + WorkflowService.populate_form_with_random_data(task, api_task, required_only=False) def get_processor(self, study_model, spec_model): workflow_model = StudyService._create_workflow_model(study_model, spec_model) diff --git a/tests/test_workflow_service.py b/tests/test_workflow_service.py index 281d1756..f509f642 100644 --- a/tests/test_workflow_service.py +++ b/tests/test_workflow_service.py @@ -77,5 +77,5 @@ class TestWorkflowService(BaseTest): processor.do_engine_steps() task = processor.next_task() task_api = WorkflowService.spiff_task_to_api_task(task, add_docs_and_forms=True) - WorkflowService.populate_form_with_random_data(task, task_api) + WorkflowService.populate_form_with_random_data(task, task_api, required_only=False) self.assertTrue(isinstance(task.data["sponsor"], dict)) \ No newline at end of file diff --git a/tests/test_workflow_spec_validation_api.py b/tests/test_workflow_spec_validation_api.py index d46746dc..1594d681 100644 --- a/tests/test_workflow_spec_validation_api.py +++ b/tests/test_workflow_spec_validation_api.py @@ -95,4 +95,17 @@ class TestWorkflowSpecValidation(BaseTest): spec_model = self.load_test_spec('repeat_form') final_data = WorkflowService.test_spec(spec_model.id) self.assertIsNotNone(final_data) - self.assertIn('cats', final_data) \ No newline at end of file + self.assertIn('cats', final_data) + + def test_required_fields(self): + self.load_example_data() + spec_model = self.load_test_spec('required_fields') + final_data = WorkflowService.test_spec(spec_model.id) + self.assertIsNotNone(final_data) + self.assertIn('string_required', final_data) + self.assertIn('string_not_required', final_data) + + final_data = WorkflowService.test_spec(spec_model.id, required_only=True) + self.assertIsNotNone(final_data) + self.assertIn('string_required', final_data) + self.assertNotIn('string_not_required', final_data) From 98fb305868f21f033d7e419f29ecc8cc6eb2ed93 Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Sat, 30 May 2020 18:43:20 -0400 Subject: [PATCH 03/18] Run the validation process twice, each time it is requested, first populating everything, and then a second time using on the required form fields. --- crc/api/workflow.py | 7 +- crc/services/study_service.py | 1 + .../research_rampup/ResearchRampUpPlan.docx | Bin 58311 -> 58518 bytes .../bpmn/research_rampup/research_rampup.bpmn | 236 +++++++++++------- tests/base_test.py | 10 +- tests/test_workflow_spec_validation_api.py | 16 +- 6 files changed, 170 insertions(+), 100 deletions(-) diff --git a/crc/api/workflow.py b/crc/api/workflow.py index 9d6e4680..8b3758d8 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -42,10 +42,15 @@ def validate_workflow_specification(spec_id): errors = [] try: - # Run the validation twice, the second time, just populate the required fields. WorkflowService.test_spec(spec_id) + except ApiError as ae: + ae.message = "When populating all fields ... " + ae.message + errors.append(ae) + try: + # Run the validation twice, the second time, just populate the required fields. WorkflowService.test_spec(spec_id, required_only=True) except ApiError as ae: + ae.message = "When populating only required fields ... " + ae.message errors.append(ae) return ApiErrorSchema(many=True).dump(errors) diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 424a911f..6dea83a9 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -175,6 +175,7 @@ class StudyService(object): return documents + @staticmethod def get_investigators(study_id): diff --git a/crc/static/bpmn/research_rampup/ResearchRampUpPlan.docx b/crc/static/bpmn/research_rampup/ResearchRampUpPlan.docx index 0c555fdb102d4f49e72dfc17265a2d7db360182b..2ff0ed801e4f0fa2ffcfc06ef5901eb86ef1689f 100644 GIT binary patch delta 22146 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 delta 22049 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 - + SequenceFlow_05ja25w @@ -34,7 +34,7 @@ Schools are developing a process for the approval of ramp up requests and enforc 1. The Research Ramp-up Plan allows for one request to be entered for a single Principle Investigator. In the form that follows enter the Primary Investigator this request is for and other identifying information. The PI's School and Supervisor will be used as needed for approval routing. 2. Provide all available information in the forms that follow to provide an overview of where the research will resume, who will be involved, what supporting resources will be needed and what steps have been taken to assure compliance with [Research Ramp-up Guidance](https://research.virginia.edu/research-ramp-guidance). 3. After all forms have been completed, you will be presented with the option to create your Research Recovery Plan in Word format. Download the document and review it. If you see any corrections that need to be made, return to the corresponding form and make the correction. -4. Once the generated Research Recovery Plan is finalize, proceed to the Plan Submission step to submit your plan for approval. +4. Once the generated Research Recovery Plan is finalized, proceed to the Plan Submission step to submit your plan for approval. SequenceFlow_05ja25w SequenceFlow_0h50bp3 @@ -47,6 +47,7 @@ Enter the following information for the PI submitting this request + @@ -60,6 +61,9 @@ Enter the following information for the PI submitting this request + + + @@ -123,7 +127,7 @@ Enter the following information for the PI submitting this request - + @@ -135,7 +139,7 @@ Enter the following information for the PI submitting this request #### People for whom you are requesting access -Provide information on all researchers you are requesting approval for reentry into the previously entered lab/research and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). +Provide information on all researchers you are requesting approval for reentry into the previously entered lab, workspace and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). **Note: no undergraduates will be allowed to work on-Grounds during Phase I.** @@ -155,12 +159,21 @@ No shared space entered {% endfor %} + + + + + + + - + + + @@ -168,7 +181,7 @@ No shared space entered - + @@ -182,7 +195,7 @@ No shared space entered - + @@ -193,17 +206,12 @@ No shared space entered - + - - - - - Flow_1eiud85 @@ -232,8 +240,8 @@ No shared space entered Flow_12ie6w0 - #### End of Workflow -Place instruction here, + #### End of Research Ramp-up Plan Workflow +Thank you for participating, Flow_05w8yd6 @@ -250,7 +258,7 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - + @@ -277,8 +285,9 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - + + @@ -304,6 +313,7 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a + @@ -345,9 +355,9 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, Click Save to skip this section and proceed to the Shared Space section. - + - + @@ -366,7 +376,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + @@ -374,19 +384,21 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp + - + - + + @@ -410,11 +422,12 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + + @@ -452,7 +465,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + #### Distancing requirements: Maintain social distancing by designing space between people to be at least 9 feet during prolonged work which will be accomplished by restricting the number of people in the lab to a density of ~250 sq. ft. /person in lab areas. When moving around, a minimum of 6 feet social distancing is required. Ideally only one person per lab bench and not more than one person can work at the same time in the same bay. @@ -475,7 +488,7 @@ Maintain social distancing by designing space between people to be at least 9 fe - Flow_1nbjr72 + Flow_097fpi3 Flow_0p2r1bo Flow_0mkh1wn Flow_1yqkpgu @@ -488,8 +501,7 @@ Maintain social distancing by designing space between people to be at least 9 fe - Indicate total square footage for every lab/space that you are requesting adding personnel to in this application. If you would like help obtaining a floor plan for your lab, your department or deans office can help. You can also create a hand drawing/block diagram of your space and the location of objects on a graph paper. - Upload your physical layout and workspace organization in the form of a jpg image or a pdf file. This can be hand-drawn or actual floor plans. - Show and/or describe designated work location for each member (during their shift) in the lab when multiple members are present at a time to meet the distancing guidelines. -- Provide a foot traffic plan (on the schematic) to indicate how people can move around while maintaining distancing requirements. This can be a freeform sketch on your floor plan showing where foot traffic can occur in your lab, and conditions, if any, to ensure distancing at all times. (e.g., direction to walk around a lab bench, rules for using shared equipment located in the lab, certain areas of lab prohibited from access, etc.). -- Provide your initial weekly laboratory schedule (see excel template) for all members that you are requesting access for, indicating all shifts as necessary. If schedule changes, please submit your revised schedule through the web portal. +- Provide a foot traffic plan (on the schematic) to indicate how people can move around while maintaining distancing requirements. This can be a freeform sketch on your floor plan showing where foot traffic can occur in your lab, and conditions, if any, to ensure distancing at all times. (e.g., direction to walk around a lab bench, rules for using shared equipment located in the lab, certain areas of lab prohibited from access, etc.). @@ -508,7 +520,7 @@ Maintain social distancing by designing space between people to be at least 9 fe Flow_0zrsh65 - + #### Health Safety Requirements: Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/url?q=http://ehs.virginia.edu/files/Lab-Safety-Plan-During-COVID-19.docx&source=gmail&ust=1590687968958000&usg=AFQjCNE83uGDFtxGkKaxjuXGhTocu-FDmw) to create and upload a copy of your laboratory policy statement to all members which includes at a minimum the following details: - Laboratory face covering rules, use of other PPE use as required @@ -519,7 +531,7 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - Where and how to obtain PPE including face covering - + Flow_1yqkpgu @@ -584,7 +596,7 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - + Flow_0zrsh65 Flow_0tz5c2v @@ -592,12 +604,16 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur Flow_0qbi47d Flow_06873ag - + + #### Script Task + + +This step is internal to the system and do not require and user interaction Flow_06873ag Flow_0aqgwvu CompleteTemplate ResearchRampUpPlan.docx RESEARCH_RAMPUP - + @@ -635,6 +651,10 @@ If a rejection notification is received, go back to the first step that needs to + #### Business Rule Task + + +This step is internal to the system and do not require and user interaction Flow_1e2qi9s Flow_08njvvi @@ -671,98 +691,129 @@ No shared space entered Flow_0cpmvcw + #### Script Task + + +This step is internal to the system and do not require and user interaction Flow_0j4rs82 Flow_07ge8uf RequestApproval ApprvlApprvr1 ApprvlApprvr2 + #### Script Task + + +This step is internal to the system and do not require and user interaction Flow_16y8glw Flow_1v7r1tg UpdateStudy title:PIComputingID.label pi:PIComputingID.value + + + #### Weekly Schedule +Provide your initial weekly laboratory schedule for all members that you are requesting access for, indicating all shifts as necessary. If any schedule changes after approval, please submit your revised schedule here for re-approval. + + + + + + + + + + + + + Flow_1nbjr72 + Flow_097fpi3 + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + @@ -813,8 +864,8 @@ No shared space entered - - + + @@ -840,10 +891,10 @@ No shared space entered - + - + @@ -861,47 +912,50 @@ No shared space entered - + - + - + - + - + - + - + - - + + - + - + - + - + - + + + + diff --git a/tests/base_test.py b/tests/base_test.py index f8ffd1ca..f0418343 100644 --- a/tests/base_test.py +++ b/tests/base_test.py @@ -115,15 +115,17 @@ class BaseTest(unittest.TestCase): self.assertIsNotNone(user_model.display_name) return dict(Authorization='Bearer ' + user_model.encode_auth_token().decode()) - def load_example_data(self, use_crc_data=False): + def load_example_data(self, use_crc_data=False, use_rrt_data=False): """use_crc_data will cause this to load the mammoth collection of documents - we built up developing crc, otherwise it depends on a small setup for - running tests.""" + we built up developing crc, use_rrt_data will do the same for hte rrt project, + otherwise it depends on a small setup for running tests.""" from example_data import ExampleDataLoader ExampleDataLoader.clean_db() - if(use_crc_data): + if use_crc_data: ExampleDataLoader().load_all() + elif use_rrt_data: + ExampleDataLoader().load_rrt() else: ExampleDataLoader().load_test_data() diff --git a/tests/test_workflow_spec_validation_api.py b/tests/test_workflow_spec_validation_api.py index 1594d681..e2f652d9 100644 --- a/tests/test_workflow_spec_validation_api.py +++ b/tests/test_workflow_spec_validation_api.py @@ -49,6 +49,13 @@ class TestWorkflowSpecValidation(BaseTest): self.load_example_data(use_crc_data=True) app.config['PB_ENABLED'] = True + self.validate_all_loaded_workflows() + + def test_successful_validation_of_rrt_workflows(self): + self.load_example_data(use_rrt_data=True) + self.validate_all_loaded_workflows() + + def validate_all_loaded_workflows(self): workflows = session.query(WorkflowSpecModel).all() errors = [] for w in workflows: @@ -59,15 +66,16 @@ class TestWorkflowSpecValidation(BaseTest): errors.extend(ApiErrorSchema(many=True).load(json_data)) self.assertEqual(0, len(errors), json.dumps(errors)) + def test_invalid_expression(self): self.load_example_data() errors = self.validate_workflow("invalid_expression") - self.assertEqual(1, len(errors)) + self.assertEqual(2, len(errors)) self.assertEqual("workflow_execution_exception", errors[0]['code']) self.assertEqual("ExclusiveGateway_003amsm", errors[0]['task_id']) self.assertEqual("Has Bananas Gateway", errors[0]['task_name']) self.assertEqual("invalid_expression.bpmn", errors[0]['file_name']) - self.assertEqual('ExclusiveGateway_003amsm: Error evaluating expression \'this_value_does_not_exist==true\', ' + self.assertEqual('When populating all fields ... ExclusiveGateway_003amsm: Error evaluating expression \'this_value_does_not_exist==true\', ' 'name \'this_value_does_not_exist\' is not defined', errors[0]["message"]) self.assertIsNotNone(errors[0]['task_data']) self.assertIn("has_bananas", errors[0]['task_data']) @@ -75,7 +83,7 @@ class TestWorkflowSpecValidation(BaseTest): def test_validation_error(self): self.load_example_data() errors = self.validate_workflow("invalid_spec") - self.assertEqual(1, len(errors)) + self.assertEqual(2, len(errors)) self.assertEqual("workflow_validation_error", errors[0]['code']) self.assertEqual("StartEvent_1", errors[0]['task_id']) self.assertEqual("invalid_spec.bpmn", errors[0]['file_name']) @@ -83,7 +91,7 @@ class TestWorkflowSpecValidation(BaseTest): def test_invalid_script(self): self.load_example_data() errors = self.validate_workflow("invalid_script") - self.assertEqual(1, len(errors)) + self.assertEqual(2, len(errors)) self.assertEqual("workflow_execution_exception", errors[0]['code']) self.assertTrue("NoSuchScript" in errors[0]['message']) self.assertEqual("Invalid_Script_Task", errors[0]['task_id']) From 2bc735a3f01ffd961370f99e5ede46b7bef3ed2a Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Sun, 31 May 2020 13:48:00 -0400 Subject: [PATCH 04/18] Kelly wrote a beautiful method for resetting the workflow that doesn't loose data when reset inside a parallel task, all I needed to do was use it. Catching TypeErrors and reporting them back to the UI so we don't 500 in a bad way (but we still 500) --- crc/services/workflow_processor.py | 28 +++-- crc/services/workflow_service.py | 5 +- .../bpmn/research_rampup/research_rampup.bpmn | 104 ++++++++++-------- 3 files changed, 80 insertions(+), 57 deletions(-) diff --git a/crc/services/workflow_processor.py b/crc/services/workflow_processor.py index d032b94a..93590d94 100644 --- a/crc/services/workflow_processor.py +++ b/crc/services/workflow_processor.py @@ -299,21 +299,27 @@ class WorkflowProcessor(object): return WorkflowStatus.waiting def hard_reset(self): - """Recreate this workflow, but keep the data from the last completed task and add it back into the first task. - This may be useful when a workflow specification changes, and users need to review all the - prior steps, but don't need to reenter all the previous data. + """Recreate this workflow, but keep the data from the last completed task and add + it back into the first task. This may be useful when a workflow specification changes, + and users need to review all the prior steps, but they don't need to reenter all the previous data. Returns the new version. """ + + # Create a new workflow based on the latest specs. self.spec_data_files = FileService.get_spec_data_files(workflow_spec_id=self.workflow_spec_id) - spec = WorkflowProcessor.get_spec(self.spec_data_files, self.workflow_spec_id) - # spec = WorkflowProcessor.get_spec(self.workflow_spec_id, version) - bpmn_workflow = BpmnWorkflow(spec, script_engine=self._script_engine) - bpmn_workflow.data = self.bpmn_workflow.data - for task in bpmn_workflow.get_tasks(SpiffTask.READY): - task.data = self.bpmn_workflow.last_task.data - bpmn_workflow.do_engine_steps() - self.bpmn_workflow = bpmn_workflow + new_spec = WorkflowProcessor.get_spec(self.spec_data_files, self.workflow_spec_id) + new_bpmn_workflow = BpmnWorkflow(new_spec, script_engine=self._script_engine) + new_bpmn_workflow.data = self.bpmn_workflow.data + + # Reset the current workflow to the beginning - which we will consider to be the first task after the root + # element. This feels a little sketchy, but I think it is safe to assume root will have one child. + first_task = self.bpmn_workflow.task_tree.children[0] + first_task.reset_token(reset_data=False) + for task in new_bpmn_workflow.get_tasks(SpiffTask.READY): + task.data = first_task.data + new_bpmn_workflow.do_engine_steps() + self.bpmn_workflow = new_bpmn_workflow def get_status(self): return self.status_of(self.bpmn_workflow) diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index dc900400..03a23aac 100644 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -294,10 +294,11 @@ class WorkflowService(object): template = Template(raw_doc) return template.render(**spiff_task.data) except jinja2.exceptions.TemplateError as ue: - - # return "Error processing template. %s" % ue.message raise ApiError(code="template_error", message="Error processing template for task %s: %s" % (spiff_task.task_spec.name, str(ue)), status_code=500) + except TypeError as te: + raise ApiError(code="template_error", message="Error processing template for task %s: %s" % + (spiff_task.task_spec.name, str(te)), status_code=500) # TODO: Catch additional errors and report back. @staticmethod diff --git a/crc/static/bpmn/research_rampup/research_rampup.bpmn b/crc/static/bpmn/research_rampup/research_rampup.bpmn index 93c4dd6d..03d82d00 100644 --- a/crc/static/bpmn/research_rampup/research_rampup.bpmn +++ b/crc/static/bpmn/research_rampup/research_rampup.bpmn @@ -72,6 +72,9 @@ Enter the following information for the PI submitting this request + + + @@ -81,6 +84,9 @@ Enter the following information for the PI submitting this request + + + @@ -89,6 +95,9 @@ Enter the following information for the PI submitting this request + + + @@ -97,6 +106,9 @@ Enter the following information for the PI submitting this request + + + @@ -105,6 +117,9 @@ Enter the following information for the PI submitting this request + + + @@ -113,18 +128,25 @@ Enter the following information for the PI submitting this request + + + + + + + @@ -138,42 +160,34 @@ Enter the following information for the PI submitting this request - #### People for whom you are requesting access -Provide information on all researchers you are requesting approval for reentry into the previously entered lab, workspace and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). + #### Personnel for whom you are requesting access +Provide information on all personnel you are requesting approval for reentry into the previously entered lab, workspace and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). **Note: no undergraduates will be allowed to work on-Grounds during Phase I.** #### Exclusive Space previously entered -{% for es in exclusive %} -{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label }} -{% else %} -No exclusive space entered -{% endfor %} - +{%+ for es in exclusive %}{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label }}{% if loop.last %}{% else %}, {% endif %}{% else %}No exclusive space entered{% endfor %} #### Shared Space previously entered -{% for ss in shared %} -{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }} -{% else %} -No shared space entered -{% endfor %} +{%+ for ss in shared %}{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }}{% if loop.last %}{% else %}, {% endif %}{% else %}No shared space entered.{% endfor %} - - + + - + + @@ -181,7 +195,7 @@ No shared space entered - + @@ -192,10 +206,8 @@ No shared space entered - - - - + + @@ -206,7 +218,7 @@ No shared space entered - + @@ -218,7 +230,7 @@ No shared space entered Flow_1nbjr72 - #### If applicable, provide a list of any [Core Resources](https://research.virginia.edu/research-core-resources) you will utilize space or instruments in and name/email of contact person in the core you have coordinated your plan with. (Core facility managers are responsible for developing a plan for their space) + If applicable, provide a list of any [Core Resources](https://research.virginia.edu/research-core-resources) utilization of space and/or instruments along with the name(s) and email(s) of contact person(s) in the core with whom you have coordinated your plan. (Core facility managers are responsible for developing a plan for their space) @@ -232,6 +244,7 @@ No shared space entered + @@ -263,6 +276,7 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a + @@ -352,7 +366,8 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a #### Space managed exclusively by {{ PIComputingID.label }} -Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, Click Save to skip this section and proceed to the Shared Space section. + +Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, click Save to skip this section and proceed to the Shared Space section. @@ -362,6 +377,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp + @@ -465,7 +481,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + #### Distancing requirements: Maintain social distancing by designing space between people to be at least 9 feet during prolonged work which will be accomplished by restricting the number of people in the lab to a density of ~250 sq. ft. /person in lab areas. When moving around, a minimum of 6 feet social distancing is required. Ideally only one person per lab bench and not more than one person can work at the same time in the same bay. @@ -496,7 +512,7 @@ Maintain social distancing by designing space between people to be at least 9 fe - Describe physical work arrangements for each lab. Show schematic of the lab and space organization to meet the distancing guidelines (see key safety expectations for ramp-up). + Describe physical work arrangements for each lab, workspace and/or office space previously entered. Show schematic of the space organization to meet the distancing guidelines (see key safety expectations for ramp-up). - Show gross dimensions, location of desks, and equipment in blocks (not details) that show available space for work and foot traffic. - Indicate total square footage for every lab/space that you are requesting adding personnel to in this application. If you would like help obtaining a floor plan for your lab, your department or deans office can help. You can also create a hand drawing/block diagram of your space and the location of objects on a graph paper. - Upload your physical layout and workspace organization in the form of a jpg image or a pdf file. This can be hand-drawn or actual floor plans. @@ -508,6 +524,7 @@ Maintain social distancing by designing space between people to be at least 9 fe + @@ -586,7 +603,7 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur #### By submitting this request, you understand that every member listed in this form for on Grounds laboratory access will: -- Complete online COVID awareness & precaution training module (link forthcoming-May 25) +- Complete [online COVID awareness & precaution training module](https://researchcompliance.web.virginia.edu/training_html5/module_content/154/index.cfm) - Complete daily health acknowledgement form signed (electronically) –email generated daily to those listed on your plan for access to on Grounds lab/research space - Fill out daily work attendance log for all lab members following your school process to check-in and out of work each day. Flow_08njvvi @@ -673,20 +690,14 @@ If notification is received that the Research Ramp-up Plan approval process is n Notify the Area Monitor for -#### Exclusive Space Area Monitors -{% for es in exclusive %} -{{ es.ExclusiveSpaceAMComputingID.data.display_name }} -{% else %} -No exclusive space entered -{% endfor %} +#### Exclusive Space previously entered +{%+ for es in exclusive %}{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label + " - " }}{% if es.ExclusiveSpaceAMComputingID is none %}No Area Monitor entered{% else %}{{ es.ExclusiveSpaceAMComputingID.label }}{% endif %}{% if loop.last %}{% else %}, {% endif %}{% else %}No exclusive space entered{% endfor %} -#### Shared Space Area Monitors -{% for ss in shared %} -{{ ss.SharedSpaceAMComputingID.data.display_name }} -{% else %} -No shared space entered -{% endfor %} + + +#### Shared Space previously entered +{%+ for ss in shared %}{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }}{% if ss.SharedSpaceAMComputingID is none %}No Area Monitor entered{% else %}{{ ss.SharedSpaceAMComputingID.label }}{% endif %}{% if loop.last %}{% else %}, {% endif %}{% else %}No shared space entered.{% endfor %} Flow_1ufh44h Flow_0cpmvcw @@ -709,10 +720,15 @@ This step is internal to the system and do not require and user interactionFlow_1v7r1tg UpdateStudy title:PIComputingID.label pi:PIComputingID.value - - - #### Weekly Schedule -Provide your initial weekly laboratory schedule for all members that you are requesting access for, indicating all shifts as necessary. If any schedule changes after approval, please submit your revised schedule here for re-approval. + + + #### Weekly Personnel Schedule(s) +Provide initial weekly schedule(s) for the PI and all personnel for whom access has been requested, indicating each space they will be working in and all shifts, if applicable. + +##### Personnel and spaces they will work in previously entered +{%+ for p in personnel %}{{ p.PersonnelComputingID.label + " - " + p.PersonnelSpace }}{% if loop.last %}{% else %}; {% endif %}{% endfor %} + +**Note:** If any schedule changes after approval, please re-submit revised schedule(s) here for re-approval. @@ -953,7 +969,7 @@ Provide your initial weekly laboratory schedule for all members that you are req - + From 26809d14706e465e88770830ef61cc097b564f3b Mon Sep 17 00:00:00 2001 From: Carlos Lopez Date: Sun, 31 May 2020 13:35:42 -0600 Subject: [PATCH 05/18] Waiting status renaming --- crc/models/approval.py | 2 +- crc/services/approval_service.py | 2 +- tests/test_approvals_api.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crc/models/approval.py b/crc/models/approval.py index f7aa2e06..c469025b 100644 --- a/crc/models/approval.py +++ b/crc/models/approval.py @@ -14,7 +14,7 @@ from crc.services.ldap_service import LdapService class ApprovalStatus(enum.Enum): - WAITING = "WAITING" # no one has done jack. + PENDING = "PENDING" # no one has done jack. APPROVED = "APPROVED" # approved by the reviewer DECLINED = "DECLINED" # rejected by the reviewer CANCELED = "CANCELED" # The document was replaced with a new version and this review is no longer needed. diff --git a/crc/services/approval_service.py b/crc/services/approval_service.py index 8a13e6c2..bd272585 100644 --- a/crc/services/approval_service.py +++ b/crc/services/approval_service.py @@ -78,7 +78,7 @@ class ApprovalService(object): version = 1 model = ApprovalModel(study_id=study_id, workflow_id=workflow_id, - approver_uid=approver_uid, status=ApprovalStatus.WAITING.value, + approver_uid=approver_uid, status=ApprovalStatus.PENDING.value, message="", date_created=datetime.now(), version=version) approval_files = ApprovalService._create_approval_files(workflow_data_files, model) diff --git a/tests/test_approvals_api.py b/tests/test_approvals_api.py index 393831e7..b9b6d226 100644 --- a/tests/test_approvals_api.py +++ b/tests/test_approvals_api.py @@ -45,7 +45,7 @@ class TestApprovals(BaseTest): study=self.study, workflow=self.workflow, approver_uid='arc93', - status=ApprovalStatus.WAITING.value, + status=ApprovalStatus.PENDING.value, version=1 ) session.add(self.approval) @@ -54,7 +54,7 @@ class TestApprovals(BaseTest): study=self.study, workflow=self.workflow, approver_uid='dhf8r', - status=ApprovalStatus.WAITING.value, + status=ApprovalStatus.PENDING.value, version=1 ) session.add(self.approval_2) @@ -98,7 +98,7 @@ class TestApprovals(BaseTest): data = dict(APPROVAL_PAYLOAD) data['id'] = approval_id - self.assertEqual(self.approval.status, ApprovalStatus.WAITING.value) + self.assertEqual(self.approval.status, ApprovalStatus.PENDING.value) rv = self.app.put(f'/v1.0/approval/{approval_id}', content_type="application/json", From 2e54f070959d23b0a73a76fb6026257b9fca53ed Mon Sep 17 00:00:00 2001 From: Carlos Lopez Date: Sun, 31 May 2020 17:24:23 -0600 Subject: [PATCH 06/18] Adding more info to files and renaming approval waiting status --- crc/api/approval.py | 1 + crc/models/approval.py | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crc/api/approval.py b/crc/api/approval.py index 739773c1..adc1fc78 100644 --- a/crc/api/approval.py +++ b/crc/api/approval.py @@ -11,6 +11,7 @@ def get_approvals(approver_uid = None): else: db_approvals = ApprovalService.get_approvals_per_user(approver_uid) approvals = [Approval.from_model(approval_model) for approval_model in db_approvals] + results = ApprovalSchema(many=True).dump(approvals) return results diff --git a/crc/models/approval.py b/crc/models/approval.py index c469025b..497a6bbf 100644 --- a/crc/models/approval.py +++ b/crc/models/approval.py @@ -11,6 +11,7 @@ from crc.models.file import FileDataModel from crc.models.study import StudyModel from crc.models.workflow import WorkflowModel from crc.services.ldap_service import LdapService +from crc.services.file_service import FileService class ApprovalStatus(enum.Enum): @@ -84,11 +85,29 @@ class Approval(object): instance.approver['title'] = user_info.title instance.approver['department'] = user_info.department + # TODO: Organize it properly, move it to services + doc_dictionary = FileService.get_reference_data(FileService.DOCUMENT_LIST, 'code', ['id']) + instance.associated_files = [] for approval_file in model.approval_files: + try: + extra_info = doc_dictionary[approval_file.file_data.file_model.irb_doc_code] + except: + extra_info = None associated_file = {} associated_file['id'] = approval_file.file_data.file_model.id - associated_file['name'] = approval_file.file_data.file_model.name + if extra_info: + categories = [extra_info['category1'], extra_info['category2'], extra_info['category3']] + # Clear empty values + categories = list(filter(lambda x: x != '' and x != 'NULL', categories)) + # Replace spaces with underscores and lowercase + categories = ['_'.join(category.split()) for category in categories] + categories = '_'.join(categories).lower() + associated_file['name'] = '_'.join((categories, approval_file.file_data.file_model.name)) + associated_file['description'] = extra_info + else: + associated_file['name'] = approval_file.file_data.file_model.name + associated_file['description'] = 'No description available' associated_file['content_type'] = approval_file.file_data.file_model.content_type instance.associated_files.append(associated_file) From dd6c1d2b42a12cd88bf3469be1d876219b075ef8 Mon Sep 17 00:00:00 2001 From: Carlos Lopez Date: Sun, 31 May 2020 18:16:42 -0600 Subject: [PATCH 07/18] Renaming approval files --- crc/models/approval.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crc/models/approval.py b/crc/models/approval.py index 497a6bbf..1f7eed38 100644 --- a/crc/models/approval.py +++ b/crc/models/approval.py @@ -68,10 +68,10 @@ class Approval(object): if model.study: instance.title = model.study.title + principal_investigator_id = model.study.primary_investigator_id instance.approver = {} try: ldap_service = LdapService() - principal_investigator_id = model.study.primary_investigator_id user_info = ldap_service.user_info(principal_investigator_id) except (ApiError, LDAPSocketOpenError) as exception: user_info = None @@ -97,17 +97,13 @@ class Approval(object): associated_file = {} associated_file['id'] = approval_file.file_data.file_model.id if extra_info: - categories = [extra_info['category1'], extra_info['category2'], extra_info['category3']] - # Clear empty values - categories = list(filter(lambda x: x != '' and x != 'NULL', categories)) - # Replace spaces with underscores and lowercase - categories = ['_'.join(category.split()) for category in categories] - categories = '_'.join(categories).lower() - associated_file['name'] = '_'.join((categories, approval_file.file_data.file_model.name)) - associated_file['description'] = extra_info + irb_doc_code = approval_file.file_data.file_model.irb_doc_code + associated_file['name'] = '_'.join((irb_doc_code, approval_file.file_data.file_model.name)) + associated_file['description'] = extra_info['description'] else: associated_file['name'] = approval_file.file_data.file_model.name associated_file['description'] = 'No description available' + associated_file['name'] = '(' + principal_investigator_id + ')' + associated_file['name'] associated_file['content_type'] = approval_file.file_data.file_model.content_type instance.associated_files.append(associated_file) From 9c7de39b094d3cd0df9eaa3f8e4778a7e2d61184 Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Sun, 31 May 2020 21:15:40 -0400 Subject: [PATCH 08/18] This adds additional file data details to the study model as well. --- crc/api/file.py | 5 +++-- crc/models/file.py | 14 ++++++++++++-- crc/models/study.py | 11 ++++++----- crc/services/file_service.py | 8 ++++++++ crc/services/study_service.py | 8 ++++++-- tests/test_study_api.py | 29 +++++++++++++++++++++++++++-- 6 files changed, 62 insertions(+), 13 deletions(-) diff --git a/crc/api/file.py b/crc/api/file.py index 07ced388..a537cfe5 100644 --- a/crc/api/file.py +++ b/crc/api/file.py @@ -12,8 +12,9 @@ from crc.services.file_service import FileService def to_file_api(file_model): - """Converts a FileModel object to something we can return via the aip""" - return File.from_models(file_model, FileService.get_file_data(file_model.id)) + """Converts a FileModel object to something we can return via the api""" + return File.from_models(file_model, FileService.get_file_data(file_model.id), + FileService.get_doc_dictionary()) def get_files(workflow_spec_id=None, workflow_id=None, form_field_key=None): diff --git a/crc/models/file.py b/crc/models/file.py index 184979e6..9cbfb7fc 100644 --- a/crc/models/file.py +++ b/crc/models/file.py @@ -86,7 +86,7 @@ class FileModel(db.Model): class File(object): @classmethod - def from_models(cls, model: FileModel, data_model: FileDataModel): + def from_models(cls, model: FileModel, data_model: FileDataModel, doc_dictionary): instance = cls() instance.id = model.id instance.name = model.name @@ -99,6 +99,15 @@ class File(object): instance.workflow_id = model.workflow_id instance.irb_doc_code = model.irb_doc_code instance.type = model.type + if model.irb_doc_code and model.irb_doc_code in doc_dictionary: + instance.category = "/".join(filter(None, [doc_dictionary[model.irb_doc_code]['category1'], + doc_dictionary[model.irb_doc_code]['category2'], + doc_dictionary[model.irb_doc_code]['category3']])) + instance.description = doc_dictionary[model.irb_doc_code]['description'] + instance.download_name = ".".join([instance.category, model.type.value]) + else: + instance.category = "" + instance.description = "" if data_model: instance.last_modified = data_model.date_created instance.latest_version = data_model.version @@ -122,7 +131,8 @@ class FileSchema(ma.Schema): model = File fields = ["id", "name", "is_status", "is_reference", "content_type", "primary", "primary_process_id", "workflow_spec_id", "workflow_id", - "irb_doc_code", "last_modified", "latest_version", "type"] + "irb_doc_code", "last_modified", "latest_version", "type", "categories", + "description", "category", "description", "download_name"] unknown = INCLUDE type = EnumField(FileType) diff --git a/crc/models/study.py b/crc/models/study.py index 38bd2f3b..21a670fe 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -5,7 +5,7 @@ from sqlalchemy import func from crc import db, ma from crc.api.common import ApiErrorSchema -from crc.models.file import FileModel, SimpleFileSchema +from crc.models.file import FileModel, SimpleFileSchema, FileSchema from crc.models.protocol_builder import ProtocolBuilderStatus, ProtocolBuilderStudy from crc.models.workflow import WorkflowSpecCategoryModel, WorkflowState, WorkflowStatus, WorkflowSpecModel, \ WorkflowModel @@ -106,7 +106,8 @@ class Study(object): def __init__(self, title, last_updated, primary_investigator_id, user_uid, id=None, protocol_builder_status=None, - sponsor="", hsr_number="", ind_number="", categories=[], **argsv): + sponsor="", hsr_number="", ind_number="", categories=[], + files=[], **argsv): self.id = id self.user_uid = user_uid self.title = title @@ -118,7 +119,7 @@ class Study(object): self.ind_number = ind_number self.categories = categories self.warnings = [] - self.files = [] + self.files = files @classmethod def from_model(cls, study_model: StudyModel): @@ -149,12 +150,12 @@ class StudySchema(ma.Schema): hsr_number = fields.String(allow_none=True) sponsor = fields.String(allow_none=True) ind_number = fields.String(allow_none=True) - files = fields.List(fields.Nested(SimpleFileSchema), dump_only=True) + files = fields.List(fields.Nested(FileSchema), dump_only=True) class Meta: model = Study additional = ["id", "title", "last_updated", "primary_investigator_id", "user_uid", - "sponsor", "ind_number"] + "sponsor", "ind_number", "files"] unknown = INCLUDE @marshmallow.post_load diff --git a/crc/services/file_service.py b/crc/services/file_service.py index 273460c1..9142a7c3 100644 --- a/crc/services/file_service.py +++ b/crc/services/file_service.py @@ -22,6 +22,14 @@ class FileService(object): DOCUMENT_LIST = "irb_documents.xlsx" INVESTIGATOR_LIST = "investigators.xlsx" + __doc_dictionary = None + + @staticmethod + def get_doc_dictionary(): + if not FileService.__doc_dictionary: + FileService.__doc_dictionary = FileService.get_reference_data(FileService.DOCUMENT_LIST, 'code', ['id']) + return FileService.__doc_dictionary + @staticmethod def add_workflow_spec_file(workflow_spec: WorkflowSpecModel, name, content_type, binary_data, primary=False, is_status=False): diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 6dea83a9..1d61c5c8 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -9,7 +9,7 @@ from ldap3.core.exceptions import LDAPSocketOpenError from crc import db, session, app from crc.api.common import ApiError -from crc.models.file import FileModel, FileModelSchema +from crc.models.file import FileModel, FileModelSchema, File from crc.models.protocol_builder import ProtocolBuilderStudy, ProtocolBuilderStatus from crc.models.stats import TaskEventModel from crc.models.study import StudyModel, Study, Category, WorkflowMetadata @@ -54,7 +54,11 @@ class StudyService(object): study = Study.from_model(study_model) study.categories = StudyService.get_categories() workflow_metas = StudyService.__get_workflow_metas(study_id) - study.files = FileService.get_files_for_study(study.id) + + files = FileService.get_files_for_study(study.id) + files = (File.from_models(model, FileService.get_file_data(model.id), + FileService.get_doc_dictionary()) for model in files) + study.files = list(files) # Calling this line repeatedly is very very slow. It creates the # master spec and runs it. diff --git a/tests/test_study_api.py b/tests/test_study_api.py index 7282ac10..2f72a7a7 100644 --- a/tests/test_study_api.py +++ b/tests/test_study_api.py @@ -1,5 +1,6 @@ import json from tests.base_test import BaseTest + from datetime import datetime, timezone from unittest.mock import patch @@ -8,8 +9,9 @@ from crc.models.protocol_builder import ProtocolBuilderStatus, \ ProtocolBuilderStudySchema from crc.models.stats import TaskEventModel from crc.models.study import StudyModel, StudySchema -from crc.models.workflow import WorkflowSpecModel, WorkflowModel, WorkflowSpecCategoryModel -from crc.services.protocol_builder import ProtocolBuilderService +from crc.models.workflow import WorkflowSpecModel, WorkflowModel +from crc.services.file_service import FileService +from crc.services.workflow_processor import WorkflowProcessor class TestStudyApi(BaseTest): @@ -68,6 +70,29 @@ class TestStudyApi(BaseTest): self.assertEqual(0, workflow["total_tasks"]) self.assertEqual(0, workflow["completed_tasks"]) + def test_get_study_has_details_about_files(self): + + # Set up the study and attach a file to it. + self.load_example_data() + self.create_reference_document() + workflow = self.create_workflow('file_upload_form') + processor = WorkflowProcessor(workflow) + task = processor.next_task() + irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs. + FileService.add_workflow_file(workflow_id=workflow.id, + name="anything.png", content_type="png", + binary_data=b'1234', irb_doc_code=irb_code) + + api_response = self.app.get('/v1.0/study/%i' % workflow.study_id, + headers=self.logged_in_headers(), content_type="application/json") + self.assert_success(api_response) + study = StudySchema().loads(api_response.get_data(as_text=True)) + self.assertEquals(1, len(study.files)) + self.assertEquals("UVA Compliance/PRC Approval", study.files[0]["category"]) + self.assertEquals("Cancer Center's PRC Approval Form", study.files[0]["description"]) + self.assertEquals("UVA Compliance/PRC Approval.png", study.files[0]["download_name"]) + + def test_add_study(self): self.load_example_data() study = self.add_test_study() From f0bd8d4f9eb74a9bc96cf0102fbe6f9d518510a8 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Sun, 31 May 2020 22:46:17 -0400 Subject: [PATCH 09/18] Adds approvals to study schema. Adds approvals endpoint --- crc/api.yml | 24 ++++++++++++++++++++++++ crc/api/approval.py | 10 +++++++++- crc/api/study.py | 6 ++---- crc/models/api_models.py | 7 ++++--- crc/models/study.py | 8 +++++--- crc/services/approval_service.py | 6 ++++++ crc/services/study_service.py | 4 ++++ 7 files changed, 54 insertions(+), 11 deletions(-) diff --git a/crc/api.yml b/crc/api.yml index edc3861b..758169b7 100644 --- a/crc/api.yml +++ b/crc/api.yml @@ -173,6 +173,30 @@ paths: application/json: schema: $ref: "#/components/schemas/Study" + /study/{study_id}/approvals: + parameters: + - name: study_id + in: path + required: true + description: The id of the study for which workflows should be returned. + schema: + type: integer + format: int32 + get: + operationId: crc.api.approval.get_approvals_for_study + summary: Returns approvals for a single study + tags: + - Studies + - Approvals + responses: + '200': + description: An array of approvals + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Approval" /workflow-specification: get: operationId: crc.api.workflow.all_specifications diff --git a/crc/api/approval.py b/crc/api/approval.py index adc1fc78..32238cf0 100644 --- a/crc/api/approval.py +++ b/crc/api/approval.py @@ -5,7 +5,7 @@ from crc.models.approval import Approval, ApprovalModel, ApprovalSchema from crc.services.approval_service import ApprovalService -def get_approvals(approver_uid = None): +def get_approvals(approver_uid=None): if not approver_uid: db_approvals = ApprovalService.get_all_approvals() else: @@ -15,6 +15,14 @@ def get_approvals(approver_uid = None): results = ApprovalSchema(many=True).dump(approvals) return results + +def get_approvals_for_study(study_id=None): + db_approvals = ApprovalService.get_approvals_for_study(study_id) + approvals = [Approval.from_model(approval_model) for approval_model in db_approvals] + results = ApprovalSchema(many=True).dump(approvals) + return results + + def update_approval(approval_id, body): if approval_id is None: raise ApiError('unknown_approval', 'Please provide a valid Approval ID.') diff --git a/crc/api/study.py b/crc/api/study.py index 423f6fe2..e9a251f8 100644 --- a/crc/api/study.py +++ b/crc/api/study.py @@ -48,12 +48,10 @@ def update_study(study_id, body): def get_study(study_id): - study_service = StudyService() - study = study_service.get_study(study_id) + study = StudyService.get_study(study_id) if (study is None): raise ApiError("Study not found", status_code=404) - schema = StudySchema() - return schema.dump(study) + return StudySchema().dump(study) def delete_study(study_id): diff --git a/crc/models/api_models.py b/crc/models/api_models.py index eee6d5f5..b8b535a7 100644 --- a/crc/models/api_models.py +++ b/crc/models/api_models.py @@ -119,7 +119,7 @@ class NavigationItemSchema(ma.Schema): class WorkflowApi(object): def __init__(self, id, status, next_task, navigation, - spec_version, is_latest_spec, workflow_spec_id, total_tasks, completed_tasks, last_updated): + spec_version, is_latest_spec, workflow_spec_id, total_tasks, completed_tasks, last_updated, title): self.id = id self.status = status self.next_task = next_task # The next task that requires user input. @@ -130,13 +130,14 @@ class WorkflowApi(object): self.total_tasks = total_tasks self.completed_tasks = completed_tasks self.last_updated = last_updated + self.title = title class WorkflowApiSchema(ma.Schema): class Meta: model = WorkflowApi fields = ["id", "status", "next_task", "navigation", "workflow_spec_id", "spec_version", "is_latest_spec", "total_tasks", "completed_tasks", - "last_updated"] + "last_updated", "title"] unknown = INCLUDE status = EnumField(WorkflowStatus) @@ -147,7 +148,7 @@ class WorkflowApiSchema(ma.Schema): def make_workflow(self, data, **kwargs): keys = ['id', 'status', 'next_task', 'navigation', 'workflow_spec_id', 'spec_version', 'is_latest_spec', "total_tasks", "completed_tasks", - "last_updated"] + "last_updated", "title"] filtered_fields = {key: data[key] for key in keys} filtered_fields['next_task'] = TaskSchema().make_task(data['next_task']) return WorkflowApi(**filtered_fields) diff --git a/crc/models/study.py b/crc/models/study.py index 21a670fe..1e1cf440 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -107,7 +107,7 @@ class Study(object): id=None, protocol_builder_status=None, sponsor="", hsr_number="", ind_number="", categories=[], - files=[], **argsv): + files=[], approvals=[], **argsv): self.id = id self.user_uid = user_uid self.title = title @@ -118,6 +118,7 @@ class Study(object): self.hsr_number = hsr_number self.ind_number = ind_number self.categories = categories + self.approvals = approvals self.warnings = [] self.files = files @@ -150,12 +151,13 @@ class StudySchema(ma.Schema): hsr_number = fields.String(allow_none=True) sponsor = fields.String(allow_none=True) ind_number = fields.String(allow_none=True) - files = fields.List(fields.Nested(FileSchema), dump_only=True) + files = fields.List(fields.Nested(SimpleFileSchema), dump_only=True) + approvals = fields.List(fields.Nested('ApprovalSchema'), dump_only=True) class Meta: model = Study additional = ["id", "title", "last_updated", "primary_investigator_id", "user_uid", - "sponsor", "ind_number", "files"] + "sponsor", "ind_number", "approvals", "files"] unknown = INCLUDE @marshmallow.post_load diff --git a/crc/services/approval_service.py b/crc/services/approval_service.py index bd272585..39886d62 100644 --- a/crc/services/approval_service.py +++ b/crc/services/approval_service.py @@ -19,6 +19,12 @@ class ApprovalService(object): db_approvals = session.query(ApprovalModel).filter_by(approver_uid=approver_uid).all() return db_approvals + @staticmethod + def get_approvals_for_study(study_id): + """Returns a list of all approvals for the given study""" + db_approvals = session.query(ApprovalModel).filter_by(study_id=study_id).all() + return db_approvals + @staticmethod def get_all_approvals(): """Returns a list of all approvlas""" diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 1d61c5c8..8fd99109 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -19,6 +19,8 @@ from crc.services.file_service import FileService from crc.services.ldap_service import LdapService from crc.services.protocol_builder import ProtocolBuilderService from crc.services.workflow_processor import WorkflowProcessor +from crc.services.approval_service import ApprovalService +from crc.models.approval import ApprovalSchema class StudyService(object): @@ -54,6 +56,8 @@ class StudyService(object): study = Study.from_model(study_model) study.categories = StudyService.get_categories() workflow_metas = StudyService.__get_workflow_metas(study_id) + study.files = FileService.get_files_for_study(study.id) + study.approvals = ApprovalService.get_approvals_for_study(study.id) files = FileService.get_files_for_study(study.id) files = (File.from_models(model, FileService.get_file_data(model.id), From cd16b984e00fef328ec31682dd161e5b75424af2 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Sun, 31 May 2020 22:46:56 -0400 Subject: [PATCH 10/18] Adds workflow spec title to workflow api schema --- crc/api/workflow.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crc/api/workflow.py b/crc/api/workflow.py index 8b3758d8..81252056 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -119,6 +119,8 @@ def __get_workflow_api_model(processor: WorkflowProcessor, next_task = None): navigation.append(NavigationItem(**nav_item)) NavigationItemSchema().dump(nav_item) + + spec = session.query(WorkflowSpecModel).filter_by(id=processor.workflow_spec_id).first() workflow_api = WorkflowApi( id=processor.get_workflow_id(), status=processor.get_status(), @@ -129,7 +131,8 @@ def __get_workflow_api_model(processor: WorkflowProcessor, next_task = None): is_latest_spec=processor.is_latest_spec, total_tasks=processor.workflow_model.total_tasks, completed_tasks=processor.workflow_model.completed_tasks, - last_updated=processor.workflow_model.last_updated + last_updated=processor.workflow_model.last_updated, + title=spec.display_name ) if not next_task: # The Next Task can be requested to be a certain task, useful for parallel tasks. # This may or may not work, sometimes there is no next task to complete. @@ -235,4 +238,4 @@ def lookup(workflow_id, field_id, query, limit): """ workflow = session.query(WorkflowModel).filter(WorkflowModel.id == workflow_id).first() lookup_data = LookupService.lookup(workflow, field_id, query, limit) - return LookupDataSchema(many=True).dump(lookup_data) \ No newline at end of file + return LookupDataSchema(many=True).dump(lookup_data) From b2e56f797b6c8306e268054b89a119d31de941b9 Mon Sep 17 00:00:00 2001 From: Carlos Lopez Date: Sun, 31 May 2020 21:02:47 -0600 Subject: [PATCH 11/18] Converting ApprovalModel to Approval in order to serialize properly the result --- crc/services/study_service.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 8fd99109..5807fb24 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -20,7 +20,7 @@ from crc.services.ldap_service import LdapService from crc.services.protocol_builder import ProtocolBuilderService from crc.services.workflow_processor import WorkflowProcessor from crc.services.approval_service import ApprovalService -from crc.models.approval import ApprovalSchema +from crc.models.approval import Approval class StudyService(object): @@ -57,7 +57,8 @@ class StudyService(object): study.categories = StudyService.get_categories() workflow_metas = StudyService.__get_workflow_metas(study_id) study.files = FileService.get_files_for_study(study.id) - study.approvals = ApprovalService.get_approvals_for_study(study.id) + approvals = ApprovalService.get_approvals_for_study(study.id) + study.approvals = [Approval.from_model(approval_model) for approval_model in approvals] files = FileService.get_files_for_study(study.id) files = (File.from_models(model, FileService.get_file_data(model.id), From 311e180c65f4945e3208ee51b0aedcb602c65bb1 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Sun, 31 May 2020 23:16:14 -0400 Subject: [PATCH 12/18] Comments out failing test for now. Adds a placeholder test for study approvals --- tests/test_study_api.py | 42 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/tests/test_study_api.py b/tests/test_study_api.py index 2f72a7a7..10f4cb6e 100644 --- a/tests/test_study_api.py +++ b/tests/test_study_api.py @@ -72,26 +72,32 @@ class TestStudyApi(BaseTest): def test_get_study_has_details_about_files(self): - # Set up the study and attach a file to it. - self.load_example_data() - self.create_reference_document() - workflow = self.create_workflow('file_upload_form') - processor = WorkflowProcessor(workflow) - task = processor.next_task() - irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs. - FileService.add_workflow_file(workflow_id=workflow.id, - name="anything.png", content_type="png", - binary_data=b'1234', irb_doc_code=irb_code) + # # Set up the study and attach a file to it. + # self.load_example_data() + # self.create_reference_document() + # workflow = self.create_workflow('file_upload_form') + # processor = WorkflowProcessor(workflow) + # task = processor.next_task() + # irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs. + # FileService.add_workflow_file(workflow_id=workflow.id, + # name="anything.png", content_type="png", + # binary_data=b'1234', irb_doc_code=irb_code) + # + # api_response = self.app.get('/v1.0/study/%i' % workflow.study_id, + # headers=self.logged_in_headers(), content_type="application/json") + # self.assert_success(api_response) + # study = StudySchema().loads(api_response.get_data(as_text=True)) + # self.assertEquals(1, len(study.files)) + # self.assertEquals("UVA Compliance/PRC Approval", study.files[0]["category"]) + # self.assertEquals("Cancer Center's PRC Approval Form", study.files[0]["description"]) + # self.assertEquals("UVA Compliance/PRC Approval.png", study.files[0]["download_name"]) - api_response = self.app.get('/v1.0/study/%i' % workflow.study_id, - headers=self.logged_in_headers(), content_type="application/json") - self.assert_success(api_response) - study = StudySchema().loads(api_response.get_data(as_text=True)) - self.assertEquals(1, len(study.files)) - self.assertEquals("UVA Compliance/PRC Approval", study.files[0]["category"]) - self.assertEquals("Cancer Center's PRC Approval Form", study.files[0]["description"]) - self.assertEquals("UVA Compliance/PRC Approval.png", study.files[0]["download_name"]) + # TODO: WRITE A TEST FOR STUDY FILES + pass + def test_get_study_has_details_about_approvals(self): + # TODO: WRITE A TEST FOR STUDY APPROVALS + pass def test_add_study(self): self.load_example_data() From bec11980eb589b4a8b0b56a6253b16ff2c6829cf Mon Sep 17 00:00:00 2001 From: Carlos Lopez Date: Sun, 31 May 2020 22:00:52 -0600 Subject: [PATCH 13/18] Fixing broken test by using proper FileSchema --- crc/models/study.py | 2 +- crc/services/study_service.py | 1 - tests/test_study_api.py | 39 +++++++++++++++++------------------ 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/crc/models/study.py b/crc/models/study.py index 1e1cf440..540ee018 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -151,7 +151,7 @@ class StudySchema(ma.Schema): hsr_number = fields.String(allow_none=True) sponsor = fields.String(allow_none=True) ind_number = fields.String(allow_none=True) - files = fields.List(fields.Nested(SimpleFileSchema), dump_only=True) + files = fields.List(fields.Nested(FileSchema), dump_only=True) approvals = fields.List(fields.Nested('ApprovalSchema'), dump_only=True) class Meta: diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 5807fb24..e6ef5291 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -56,7 +56,6 @@ class StudyService(object): study = Study.from_model(study_model) study.categories = StudyService.get_categories() workflow_metas = StudyService.__get_workflow_metas(study_id) - study.files = FileService.get_files_for_study(study.id) approvals = ApprovalService.get_approvals_for_study(study.id) study.approvals = [Approval.from_model(approval_model) for approval_model in approvals] diff --git a/tests/test_study_api.py b/tests/test_study_api.py index 10f4cb6e..61e42543 100644 --- a/tests/test_study_api.py +++ b/tests/test_study_api.py @@ -72,28 +72,27 @@ class TestStudyApi(BaseTest): def test_get_study_has_details_about_files(self): - # # Set up the study and attach a file to it. - # self.load_example_data() - # self.create_reference_document() - # workflow = self.create_workflow('file_upload_form') - # processor = WorkflowProcessor(workflow) - # task = processor.next_task() - # irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs. - # FileService.add_workflow_file(workflow_id=workflow.id, - # name="anything.png", content_type="png", - # binary_data=b'1234', irb_doc_code=irb_code) - # - # api_response = self.app.get('/v1.0/study/%i' % workflow.study_id, - # headers=self.logged_in_headers(), content_type="application/json") - # self.assert_success(api_response) - # study = StudySchema().loads(api_response.get_data(as_text=True)) - # self.assertEquals(1, len(study.files)) - # self.assertEquals("UVA Compliance/PRC Approval", study.files[0]["category"]) - # self.assertEquals("Cancer Center's PRC Approval Form", study.files[0]["description"]) - # self.assertEquals("UVA Compliance/PRC Approval.png", study.files[0]["download_name"]) + # Set up the study and attach a file to it. + self.load_example_data() + self.create_reference_document() + workflow = self.create_workflow('file_upload_form') + processor = WorkflowProcessor(workflow) + task = processor.next_task() + irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs. + FileService.add_workflow_file(workflow_id=workflow.id, + name="anything.png", content_type="png", + binary_data=b'1234', irb_doc_code=irb_code) + + api_response = self.app.get('/v1.0/study/%i' % workflow.study_id, + headers=self.logged_in_headers(), content_type="application/json") + self.assert_success(api_response) + study = StudySchema().loads(api_response.get_data(as_text=True)) + self.assertEquals(1, len(study.files)) + self.assertEquals("UVA Compliance/PRC Approval", study.files[0]["category"]) + self.assertEquals("Cancer Center's PRC Approval Form", study.files[0]["description"]) + self.assertEquals("UVA Compliance/PRC Approval.png", study.files[0]["download_name"]) # TODO: WRITE A TEST FOR STUDY FILES - pass def test_get_study_has_details_about_approvals(self): # TODO: WRITE A TEST FOR STUDY APPROVALS From 2d5eb740efa766668abacd88de2e0e29859d3fe9 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Mon, 1 Jun 2020 00:07:50 -0400 Subject: [PATCH 14/18] Updates RRTworkflow spec files --- .../research_rampup/ResearchRampUpPlan.docx | Bin 58518 -> 58311 bytes .../bpmn/research_rampup/research_rampup.bpmn | 242 +++++++----------- 2 files changed, 96 insertions(+), 146 deletions(-) diff --git a/crc/static/bpmn/research_rampup/ResearchRampUpPlan.docx b/crc/static/bpmn/research_rampup/ResearchRampUpPlan.docx index 2ff0ed801e4f0fa2ffcfc06ef5901eb86ef1689f..0c555fdb102d4f49e72dfc17265a2d7db360182b 100644 GIT binary patch delta 22049 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 diff --git a/crc/static/bpmn/research_rampup/research_rampup.bpmn b/crc/static/bpmn/research_rampup/research_rampup.bpmn index 93c4dd6d..cfd41423 100644 --- a/crc/static/bpmn/research_rampup/research_rampup.bpmn +++ b/crc/static/bpmn/research_rampup/research_rampup.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_05ja25w @@ -61,9 +61,6 @@ Enter the following information for the PI submitting this request - - - @@ -127,7 +124,7 @@ Enter the following information for the PI submitting this request - + @@ -139,7 +136,7 @@ Enter the following information for the PI submitting this request #### People for whom you are requesting access -Provide information on all researchers you are requesting approval for reentry into the previously entered lab, workspace and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). +Provide information on all researchers you are requesting approval for reentry into the previously entered lab/research and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). **Note: no undergraduates will be allowed to work on-Grounds during Phase I.** @@ -159,21 +156,13 @@ No shared space entered {% endfor %} - - - - - - - - + - @@ -181,7 +170,7 @@ No shared space entered - + @@ -195,7 +184,7 @@ No shared space entered - + @@ -206,12 +195,17 @@ No shared space entered - + + + + + + Flow_1eiud85 @@ -258,7 +252,7 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - + @@ -285,7 +279,7 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - + @@ -313,7 +307,6 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - @@ -355,9 +348,9 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, Click Save to skip this section and proceed to the Shared Space section. - + - + @@ -376,7 +369,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + @@ -384,18 +377,17 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - - + - + @@ -422,12 +414,11 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + - @@ -465,7 +456,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + #### Distancing requirements: Maintain social distancing by designing space between people to be at least 9 feet during prolonged work which will be accomplished by restricting the number of people in the lab to a density of ~250 sq. ft. /person in lab areas. When moving around, a minimum of 6 feet social distancing is required. Ideally only one person per lab bench and not more than one person can work at the same time in the same bay. @@ -488,7 +479,7 @@ Maintain social distancing by designing space between people to be at least 9 fe - Flow_097fpi3 + Flow_1nbjr72 Flow_0p2r1bo Flow_0mkh1wn Flow_1yqkpgu @@ -501,7 +492,8 @@ Maintain social distancing by designing space between people to be at least 9 fe - Indicate total square footage for every lab/space that you are requesting adding personnel to in this application. If you would like help obtaining a floor plan for your lab, your department or deans office can help. You can also create a hand drawing/block diagram of your space and the location of objects on a graph paper. - Upload your physical layout and workspace organization in the form of a jpg image or a pdf file. This can be hand-drawn or actual floor plans. - Show and/or describe designated work location for each member (during their shift) in the lab when multiple members are present at a time to meet the distancing guidelines. -- Provide a foot traffic plan (on the schematic) to indicate how people can move around while maintaining distancing requirements. This can be a freeform sketch on your floor plan showing where foot traffic can occur in your lab, and conditions, if any, to ensure distancing at all times. (e.g., direction to walk around a lab bench, rules for using shared equipment located in the lab, certain areas of lab prohibited from access, etc.). +- Provide a foot traffic plan (on the schematic) to indicate how people can move around while maintaining distancing requirements. This can be a freeform sketch on your floor plan showing where foot traffic can occur in your lab, and conditions, if any, to ensure distancing at all times. (e.g., direction to walk around a lab bench, rules for using shared equipment located in the lab, certain areas of lab prohibited from access, etc.). +- Provide your initial weekly laboratory schedule (see excel template) for all members that you are requesting access for, indicating all shifts as necessary. If schedule changes, please submit your revised schedule through the web portal. @@ -520,7 +512,7 @@ Maintain social distancing by designing space between people to be at least 9 fe Flow_0zrsh65 - + #### Health Safety Requirements: Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/url?q=http://ehs.virginia.edu/files/Lab-Safety-Plan-During-COVID-19.docx&source=gmail&ust=1590687968958000&usg=AFQjCNE83uGDFtxGkKaxjuXGhTocu-FDmw) to create and upload a copy of your laboratory policy statement to all members which includes at a minimum the following details: - Laboratory face covering rules, use of other PPE use as required @@ -531,7 +523,7 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - Where and how to obtain PPE including face covering - + Flow_1yqkpgu @@ -596,7 +588,7 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - + Flow_0zrsh65 Flow_0tz5c2v @@ -604,16 +596,12 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur Flow_0qbi47d Flow_06873ag - - #### Script Task - - -This step is internal to the system and do not require and user interaction + Flow_06873ag Flow_0aqgwvu CompleteTemplate ResearchRampUpPlan.docx RESEARCH_RAMPUP - + @@ -651,10 +639,6 @@ If a rejection notification is received, go back to the first step that needs to - #### Business Rule Task - - -This step is internal to the system and do not require and user interaction Flow_1e2qi9s Flow_08njvvi @@ -691,129 +675,98 @@ No shared space entered Flow_0cpmvcw - #### Script Task - - -This step is internal to the system and do not require and user interaction Flow_0j4rs82 Flow_07ge8uf RequestApproval ApprvlApprvr1 ApprvlApprvr2 - #### Script Task - - -This step is internal to the system and do not require and user interaction Flow_16y8glw Flow_1v7r1tg UpdateStudy title:PIComputingID.label pi:PIComputingID.value - - - #### Weekly Schedule -Provide your initial weekly laboratory schedule for all members that you are requesting access for, indicating all shifts as necessary. If any schedule changes after approval, please submit your revised schedule here for re-approval. - - - - - - - - - - - - - Flow_1nbjr72 - Flow_097fpi3 - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + @@ -864,8 +817,8 @@ Provide your initial weekly laboratory schedule for all members that you are req - - + + @@ -890,11 +843,8 @@ Provide your initial weekly laboratory schedule for all members that you are req - - - - + @@ -912,49 +862,49 @@ Provide your initial weekly laboratory schedule for all members that you are req - + - + - + - + - + - - - - - - - - - - - - + + + + + + + + + + + + - + - + - + - - + + From 7351dc4a4321474c06a130abd2fa08182f58c22b Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Mon, 1 Jun 2020 00:14:09 -0400 Subject: [PATCH 15/18] Updates RRT workflow spec files for reals this time --- .../research_rampup/ResearchRampUpPlan.docx | Bin 58311 -> 58518 bytes .../exclusive_area_monitors.dmn | 54 +++ .../bpmn/research_rampup/research_rampup.bpmn | 454 +++++++++--------- .../rrt_top_level_workflow.bpmn | 26 + .../research_rampup/shared_area_monitors.dmn | 54 +++ 5 files changed, 355 insertions(+), 233 deletions(-) create mode 100644 crc/static/bpmn/research_rampup/exclusive_area_monitors.dmn create mode 100644 crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn create mode 100644 crc/static/bpmn/research_rampup/shared_area_monitors.dmn diff --git a/crc/static/bpmn/research_rampup/ResearchRampUpPlan.docx b/crc/static/bpmn/research_rampup/ResearchRampUpPlan.docx index 0c555fdb102d4f49e72dfc17265a2d7db360182b..2ff0ed801e4f0fa2ffcfc06ef5901eb86ef1689f 100644 GIT binary patch delta 22146 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 delta 22049 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 + + + + + + len(exclusive) + + + + + sum([1 for x in exclusive if x.get('ExclusiveSpaceAMComputingID',None) == None]) + + + + + No exclusvie spaces without Area Monitor + + >0 + + + 0 + + + true + + + + One or more exclusive space without an Area Monitor + + >0 + + + > 0 + + + false + + + + No exclusive spaces entered + + 0 + + + + + + true + + + + + diff --git a/crc/static/bpmn/research_rampup/research_rampup.bpmn b/crc/static/bpmn/research_rampup/research_rampup.bpmn index cfd41423..c9b8a17c 100644 --- a/crc/static/bpmn/research_rampup/research_rampup.bpmn +++ b/crc/static/bpmn/research_rampup/research_rampup.bpmn @@ -34,7 +34,7 @@ Schools are developing a process for the approval of ramp up requests and enforc 1. The Research Ramp-up Plan allows for one request to be entered for a single Principle Investigator. In the form that follows enter the Primary Investigator this request is for and other identifying information. The PI's School and Supervisor will be used as needed for approval routing. 2. Provide all available information in the forms that follow to provide an overview of where the research will resume, who will be involved, what supporting resources will be needed and what steps have been taken to assure compliance with [Research Ramp-up Guidance](https://research.virginia.edu/research-ramp-guidance). 3. After all forms have been completed, you will be presented with the option to create your Research Recovery Plan in Word format. Download the document and review it. If you see any corrections that need to be made, return to the corresponding form and make the correction. -4. Once the generated Research Recovery Plan is finalized, proceed to the Plan Submission step to submit your plan for approval. +4. Once the generated Research Recovery Plan is finalize, proceed to the Plan Submission step to submit your plan for approval. SequenceFlow_05ja25w SequenceFlow_0h50bp3 @@ -47,7 +47,6 @@ Enter the following information for the PI submitting this request - @@ -134,7 +133,7 @@ Enter the following information for the PI submitting this request - + #### People for whom you are requesting access Provide information on all researchers you are requesting approval for reentry into the previously entered lab/research and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). @@ -162,7 +161,6 @@ No shared space entered - @@ -208,8 +206,8 @@ No shared space entered - Flow_1eiud85 - Flow_1nbjr72 + Flow_0r9cfe1 + Flow_0821rbm #### If applicable, provide a list of any [Core Resources](https://research.virginia.edu/research-core-resources) you will utilize space or instruments in and name/email of contact person in the core you have coordinated your plan with. (Core facility managers are responsible for developing a plan for their space) @@ -230,12 +228,12 @@ No shared space entered - Flow_15zy1q7 - Flow_12ie6w0 + Flow_1myg94h + Flow_0zyal9y - #### End of Research Ramp-up Plan Workflow -Thank you for participating, + #### End of Workflow +Place instruction here, Flow_05w8yd6 @@ -268,6 +266,9 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a + + + @@ -281,7 +282,6 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - @@ -321,28 +321,10 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - Flow_19xeq76 - Flow_16342pm + Flow_0ss3j2p + Flow_1myg94h - - Flow_1v7r1tg - Flow_19xeq76 - Flow_0qf2y84 - Flow_15zy1q7 - Flow_0ya8hw8 - - - Flow_0tk64b6 - Flow_12ie6w0 - Flow_0zz2hbq - Flow_16342pm - Flow_1eiud85 - - - - - #### Space managed exclusively by {{ PIComputingID.label }} Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, Click Save to skip this section and proceed to the Shared Space section. @@ -366,6 +348,9 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp + + + @@ -390,7 +375,6 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - @@ -423,12 +407,9 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - Flow_0qf2y84 - Flow_0tk64b6 + Flow_1grd970 + Flow_0ss3j2p - - - @@ -451,12 +432,9 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - Flow_0ya8hw8 - Flow_0zz2hbq + Flow_0zyal9y + Flow_0r9cfe1 - - - #### Distancing requirements: Maintain social distancing by designing space between people to be at least 9 feet during prolonged work which will be accomplished by restricting the number of people in the lab to a density of ~250 sq. ft. /person in lab areas. When moving around, a minimum of 6 feet social distancing is required. Ideally only one person per lab bench and not more than one person can work at the same time in the same bay. @@ -474,18 +452,9 @@ Maintain social distancing by designing space between people to be at least 9 fe - Flow_0p2r1bo - Flow_0tz5c2v + Flow_0821rbm + Flow_18f81dp - - - Flow_1nbjr72 - Flow_0p2r1bo - Flow_0mkh1wn - Flow_1yqkpgu - Flow_1c6m5wv - - Describe physical work arrangements for each lab. Show schematic of the lab and space organization to meet the distancing guidelines (see key safety expectations for ramp-up). - Show gross dimensions, location of desks, and equipment in blocks (not details) that show available space for work and foot traffic. @@ -508,10 +477,9 @@ Maintain social distancing by designing space between people to be at least 9 fe - Flow_0mkh1wn - Flow_0zrsh65 + Flow_18f81dp + Flow_0cp1fez - #### Health Safety Requirements: Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/url?q=http://ehs.virginia.edu/files/Lab-Safety-Plan-During-COVID-19.docx&source=gmail&ust=1590687968958000&usg=AFQjCNE83uGDFtxGkKaxjuXGhTocu-FDmw) to create and upload a copy of your laboratory policy statement to all members which includes at a minimum the following details: @@ -526,10 +494,9 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - Flow_1yqkpgu - Flow_1ox5nv6 + Flow_0cp1fez + Flow_0vpwylg - @@ -573,8 +540,8 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - Flow_1c6m5wv - Flow_0qbi47d + Flow_0vpwylg + Flow_1r2p20h #### By submitting this request, you understand that every member listed in this form for on Grounds laboratory access will: @@ -584,31 +551,25 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur Flow_08njvvi Flow_0j4rs82 - - - - - - - Flow_0zrsh65 - Flow_0tz5c2v - Flow_1ox5nv6 - Flow_0qbi47d - Flow_06873ag - - Flow_06873ag + Flow_1r2p20h Flow_0aqgwvu CompleteTemplate ResearchRampUpPlan.docx RESEARCH_RAMPUP - + #### Approval Process The Research Ramp-up Plan and associated documents will be reviewed by{{ " " + ApprvlApprvrName1 }}{{ '.' if ApprvlApprvrName2 == 'n/a' else ' and ' + ApprvlApprvrName2 + '.' }} While waiting for approval, be sure that all required training has been completed and supplies secured. When the approval email notification is received, confirming the three questions below will allow you to proceed. +{%+ set ns = namespace() %}{% set ns.exclusive = 0 %}{% set ns.shared = 0 %}{% for es in exclusive %}{% if es.ExclusiveSpaceAMComputingID is none %}{% set ns.exclusive = ns.exclusive + 1 %}{% endif %}{% endfor %}{% for ss in shared %}{% if ss.SharedSpaceAMComputingID is none %}{% set ns.shared = ns.shared + 1 %}{% endif %}{% endfor %} + + +#### Test +Missing Exclusive: {{ ns.exclusive }} +Missing Shared: {{ ns.shared }} If a rejection notification is received, go back to the first step that needs to be addressed and step through each subsequent form from that point. @@ -617,23 +578,94 @@ If a rejection notification is received, go back to the first step that needs to + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - Flow_07ge8uf + Flow_1r9hwx3 Flow_1ufh44h @@ -656,21 +688,12 @@ If notification is received that the Research Ramp-up Plan approval process is n #### Ready to Ramp-up Research Notify the Area Monitor for - -#### Exclusive Space Area Monitors -{% for es in exclusive %} -{{ es.ExclusiveSpaceAMComputingID.data.display_name }} -{% else %} -No exclusive space entered -{% endfor %} +#### Exclusive Space previously entered +{%+ for es in exclusive %}{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label + " - " }}{% if es.ExclusiveSpaceAMComputingID is none %}No Area Monitor entered{% else %}{{ es.ExclusiveSpaceAMComputingID.label }}{% endif %}{% if loop.last %}{% else %}, {% endif %}{% else %}No exclusive space entered{% endfor %} -#### Shared Space Area Monitors -{% for ss in shared %} -{{ ss.SharedSpaceAMComputingID.data.display_name }} -{% else %} -No shared space entered -{% endfor %} +#### Shared Space previously entered +{%+ for ss in shared %}{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }}{% if ss.SharedSpaceAMComputingID is none %}No Area Monitor entered{% else %}{{ ss.SharedSpaceAMComputingID.label }}{% endif %}{% if loop.last %}{% else %}, {% endif %}{% else %}No shared space entered.{% endfor %} Flow_1ufh44h Flow_0cpmvcw @@ -679,146 +702,117 @@ No shared space entered Flow_07ge8uf RequestApproval ApprvlApprvr1 ApprvlApprvr2 - Flow_16y8glw - Flow_1v7r1tg + Flow_1grd970 UpdateStudy title:PIComputingID.label pi:PIComputingID.value + + + Flow_07ge8uf + Flow_09m77pd + + + + + + + + + + + + + + Flow_09m77pd + Flow_1r9hwx3 + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - + + @@ -837,74 +831,68 @@ No shared space entered - - + + - + + + + - + - - - - - - - + - + - + - - - - + - + - + - + - - - - + - + - + - + - + - + - + - - + + + + + diff --git a/crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn b/crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn new file mode 100644 index 00000000..26b2fe37 --- /dev/null +++ b/crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn @@ -0,0 +1,26 @@ + + + + + SequenceFlow_0lvudp8 + + + + SequenceFlow_0lvudp8 + + + + + + + + + + + + + + + + + diff --git a/crc/static/bpmn/research_rampup/shared_area_monitors.dmn b/crc/static/bpmn/research_rampup/shared_area_monitors.dmn new file mode 100644 index 00000000..db3d43b3 --- /dev/null +++ b/crc/static/bpmn/research_rampup/shared_area_monitors.dmn @@ -0,0 +1,54 @@ + + + + + + + len(shared) + + + + + sum([1 for x in exclusive if x.get('SharedSpaceAMComputingID',None) == None]) + + + + + No shared spaces without Area Monitor + + >0 + + + 0 + + + true + + + + One or more shared space without an Area Monitor + + >0 + + + > 0 + + + false + + + + No shared spaces entered + + 0 + + + + + + true + + + + + From 8092bbe682ff3155539c97b923bb8063b91a1fdf Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Mon, 1 Jun 2020 00:17:20 -0400 Subject: [PATCH 16/18] Wrong file in wrong place. --- .../rrt_top_level_workflow.bpmn | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn diff --git a/crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn b/crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn deleted file mode 100644 index 26b2fe37..00000000 --- a/crc/static/bpmn/research_rampup/rrt_top_level_workflow.bpmn +++ /dev/null @@ -1,26 +0,0 @@ - - - - - SequenceFlow_0lvudp8 - - - - SequenceFlow_0lvudp8 - - - - - - - - - - - - - - - - - From ea441dbff55e44031ce8a4b101cc199dc7fa9236 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Mon, 1 Jun 2020 00:28:37 -0400 Subject: [PATCH 17/18] More updates --- .../bpmn/research_rampup/research_rampup.bpmn | 438 +++++++++++------- crc/static/reference/rrt_documents.xlsx | Bin 9812 -> 9822 bytes 2 files changed, 264 insertions(+), 174 deletions(-) diff --git a/crc/static/bpmn/research_rampup/research_rampup.bpmn b/crc/static/bpmn/research_rampup/research_rampup.bpmn index c9b8a17c..3f3b3f92 100644 --- a/crc/static/bpmn/research_rampup/research_rampup.bpmn +++ b/crc/static/bpmn/research_rampup/research_rampup.bpmn @@ -34,7 +34,7 @@ Schools are developing a process for the approval of ramp up requests and enforc 1. The Research Ramp-up Plan allows for one request to be entered for a single Principle Investigator. In the form that follows enter the Primary Investigator this request is for and other identifying information. The PI's School and Supervisor will be used as needed for approval routing. 2. Provide all available information in the forms that follow to provide an overview of where the research will resume, who will be involved, what supporting resources will be needed and what steps have been taken to assure compliance with [Research Ramp-up Guidance](https://research.virginia.edu/research-ramp-guidance). 3. After all forms have been completed, you will be presented with the option to create your Research Recovery Plan in Word format. Download the document and review it. If you see any corrections that need to be made, return to the corresponding form and make the correction. -4. Once the generated Research Recovery Plan is finalize, proceed to the Plan Submission step to submit your plan for approval. +4. Once the generated Research Recovery Plan is finalized, proceed to the Plan Submission step to submit your plan for approval. SequenceFlow_05ja25w SequenceFlow_0h50bp3 @@ -47,6 +47,7 @@ Enter the following information for the PI submitting this request + @@ -60,6 +61,9 @@ Enter the following information for the PI submitting this request + + + @@ -68,6 +72,9 @@ Enter the following information for the PI submitting this request + + + @@ -77,6 +84,9 @@ Enter the following information for the PI submitting this request + + + @@ -85,6 +95,9 @@ Enter the following information for the PI submitting this request + + + @@ -93,6 +106,9 @@ Enter the following information for the PI submitting this request + + + @@ -101,6 +117,9 @@ Enter the following information for the PI submitting this request + + + @@ -109,21 +128,28 @@ Enter the following information for the PI submitting this request + + + + + + + - + @@ -133,34 +159,35 @@ Enter the following information for the PI submitting this request - - #### People for whom you are requesting access -Provide information on all researchers you are requesting approval for reentry into the previously entered lab/research and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). + + #### Personnel for whom you are requesting access +Provide information on all personnel you are requesting approval for reentry into the previously entered lab, workspace and/or office space(s) for conducting research on-Grounds. (If there are personnel already working in the space, include them). **Note: no undergraduates will be allowed to work on-Grounds during Phase I.** #### Exclusive Space previously entered -{% for es in exclusive %} -{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label }} -{% else %} -No exclusive space entered -{% endfor %} - +{%+ for es in exclusive %}{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label }}{% if loop.last %}{% else %}, {% endif %}{% else %}No exclusive space entered{% endfor %} #### Shared Space previously entered -{% for ss in shared %} -{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }} -{% else %} -No shared space entered -{% endfor %} +{%+ for ss in shared %}{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }}{% if loop.last %}{% else %}, {% endif %}{% else %}No shared space entered.{% endfor %} + + + + + + + + + + @@ -179,9 +206,7 @@ No shared space entered - - - + @@ -199,18 +224,13 @@ No shared space entered - - - - - - Flow_0r9cfe1 - Flow_0821rbm + Flow_0hc1r8a + Flow_1yxaewj - #### If applicable, provide a list of any [Core Resources](https://research.virginia.edu/research-core-resources) you will utilize space or instruments in and name/email of contact person in the core you have coordinated your plan with. (Core facility managers are responsible for developing a plan for their space) + If applicable, provide a list of any [Core Resources](https://research.virginia.edu/research-core-resources) utilization of space and/or instruments along with the name(s) and email(s) of contact person(s) in the core with whom you have coordinated your plan. (Core facility managers are responsible for developing a plan for their space) @@ -224,16 +244,17 @@ No shared space entered + - Flow_1myg94h - Flow_0zyal9y + Flow_1n69wsr + Flow_13pusfu - #### End of Workflow -Place instruction here, + #### End of Research Ramp-up Plan Workflow +Thank you for participating, Flow_05w8yd6 @@ -250,11 +271,12 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - + + @@ -280,8 +302,9 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - + + @@ -307,6 +330,7 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a + @@ -321,22 +345,24 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a - Flow_0ss3j2p - Flow_1myg94h + Flow_0o4tg9g + Flow_1n69wsr #### Space managed exclusively by {{ PIComputingID.label }} -Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, Click Save to skip this section and proceed to the Shared Space section. + +Submit one entry for each space the PI is the exclusive investigator. If all space is shared with one or more other investigators, click Save to skip this section and proceed to the Shared Space section. - + - + + @@ -354,7 +380,7 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + @@ -362,19 +388,21 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp + - + - + + @@ -398,17 +426,18 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - + + - Flow_1grd970 - Flow_0ss3j2p + Flow_0uc4o6c + Flow_0o4tg9g @@ -432,8 +461,8 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp - Flow_0zyal9y - Flow_0r9cfe1 + Flow_13pusfu + Flow_0hc1r8a #### Distancing requirements: @@ -452,23 +481,23 @@ Maintain social distancing by designing space between people to be at least 9 fe - Flow_0821rbm - Flow_18f81dp + Flow_1itd8db + Flow_1lo964l - Describe physical work arrangements for each lab. Show schematic of the lab and space organization to meet the distancing guidelines (see key safety expectations for ramp-up). + Describe physical work arrangements for each lab, workspace and/or office space previously entered. Show schematic of the space organization to meet the distancing guidelines (see key safety expectations for ramp-up). - Show gross dimensions, location of desks, and equipment in blocks (not details) that show available space for work and foot traffic. - Indicate total square footage for every lab/space that you are requesting adding personnel to in this application. If you would like help obtaining a floor plan for your lab, your department or deans office can help. You can also create a hand drawing/block diagram of your space and the location of objects on a graph paper. - Upload your physical layout and workspace organization in the form of a jpg image or a pdf file. This can be hand-drawn or actual floor plans. - Show and/or describe designated work location for each member (during their shift) in the lab when multiple members are present at a time to meet the distancing guidelines. -- Provide a foot traffic plan (on the schematic) to indicate how people can move around while maintaining distancing requirements. This can be a freeform sketch on your floor plan showing where foot traffic can occur in your lab, and conditions, if any, to ensure distancing at all times. (e.g., direction to walk around a lab bench, rules for using shared equipment located in the lab, certain areas of lab prohibited from access, etc.). -- Provide your initial weekly laboratory schedule (see excel template) for all members that you are requesting access for, indicating all shifts as necessary. If schedule changes, please submit your revised schedule through the web portal. +- Provide a foot traffic plan (on the schematic) to indicate how people can move around while maintaining distancing requirements. This can be a freeform sketch on your floor plan showing where foot traffic can occur in your lab, and conditions, if any, to ensure distancing at all times. (e.g., direction to walk around a lab bench, rules for using shared equipment located in the lab, certain areas of lab prohibited from access, etc.). + @@ -477,10 +506,10 @@ Maintain social distancing by designing space between people to be at least 9 fe - Flow_18f81dp - Flow_0cp1fez + Flow_1lo964l + Flow_0wgdxa6 - + #### Health Safety Requirements: Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/url?q=http://ehs.virginia.edu/files/Lab-Safety-Plan-During-COVID-19.docx&source=gmail&ust=1590687968958000&usg=AFQjCNE83uGDFtxGkKaxjuXGhTocu-FDmw) to create and upload a copy of your laboratory policy statement to all members which includes at a minimum the following details: - Laboratory face covering rules, use of other PPE use as required @@ -491,11 +520,11 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - Where and how to obtain PPE including face covering - + - Flow_0cp1fez - Flow_0vpwylg + Flow_0wgdxa6 + Flow_0judgmp @@ -540,26 +569,29 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur - Flow_0vpwylg - Flow_1r2p20h + Flow_0judgmp + Flow_11uqavk #### By submitting this request, you understand that every member listed in this form for on Grounds laboratory access will: -- Complete online COVID awareness & precaution training module (link forthcoming-May 25) +- Complete [online COVID awareness & precaution training module](https://researchcompliance.web.virginia.edu/training_html5/module_content/154/index.cfm) - Complete daily health acknowledgement form signed (electronically) –email generated daily to those listed on your plan for access to on Grounds lab/research space - Fill out daily work attendance log for all lab members following your school process to check-in and out of work each day. Flow_08njvvi Flow_0j4rs82 - - Flow_1r2p20h + + #### Script Task + + +This step is internal to the system and do not require and user interaction + Flow_11uqavk Flow_0aqgwvu CompleteTemplate ResearchRampUpPlan.docx RESEARCH_RAMPUP - + - - + #### Approval Process The Research Ramp-up Plan and associated documents will be reviewed by{{ " " + ApprvlApprvrName1 }}{{ '.' if ApprvlApprvrName2 == 'n/a' else ' and ' + ApprvlApprvrName2 + '.' }} While waiting for approval, be sure that all required training has been completed and supplies secured. When the approval email notification is received, confirming the three questions below will allow you to proceed. @@ -665,12 +697,15 @@ If a rejection notification is received, go back to the first step that needs to - Flow_1r9hwx3 - Flow_1ufh44h + SequenceFlow_0qc39tw + #### Business Rule Task + + +This step is internal to the system and do not require and user interaction Flow_1e2qi9s Flow_08njvvi @@ -688,211 +723,266 @@ If notification is received that the Research Ramp-up Plan approval process is n #### Ready to Ramp-up Research Notify the Area Monitor for + #### Exclusive Space previously entered {%+ for es in exclusive %}{{ es.ExclusiveSpaceRoomID + " " + es.ExclusiveSpaceBuilding.label + " - " }}{% if es.ExclusiveSpaceAMComputingID is none %}No Area Monitor entered{% else %}{{ es.ExclusiveSpaceAMComputingID.label }}{% endif %}{% if loop.last %}{% else %}, {% endif %}{% else %}No exclusive space entered{% endfor %} + + #### Shared Space previously entered {%+ for ss in shared %}{{ ss.SharedSpaceRoomID + " " + ss.SharedSpaceBuilding.label }}{% if ss.SharedSpaceAMComputingID is none %}No Area Monitor entered{% else %}{{ ss.SharedSpaceAMComputingID.label }}{% endif %}{% if loop.last %}{% else %}, {% endif %}{% else %}No shared space entered.{% endfor %} - Flow_1ufh44h + SequenceFlow_0qc39tw Flow_0cpmvcw + #### Script Task + + +This step is internal to the system and do not require and user interaction Flow_0j4rs82 Flow_07ge8uf RequestApproval ApprvlApprvr1 ApprvlApprvr2 + #### Script Task + + +This step is internal to the system and do not require and user interaction Flow_16y8glw - Flow_1grd970 + Flow_0uc4o6c UpdateStudy title:PIComputingID.label pi:PIComputingID.value - - + + #### Weekly Personnel Schedule(s) +Provide initial weekly schedule(s) for the PI and all personnel for whom access has been requested, indicating each space they will be working in and all shifts, if applicable. + +##### Personnel and spaces they will work in previously entered +{%+ for p in personnel %}{{ p.PersonnelComputingID.label + " - " + p.PersonnelSpace }}{% if loop.last %}{% else %}; {% endif %}{% endfor %} + +**Note:** If any schedule changes after approval, please re-submit revised schedule(s) here for re-approval. + + + + + + + + + + + + + Flow_1yxaewj + Flow_1itd8db + + + + + + + + + + + + + + #### Business Rule Task + + + + +This step is internal to the system and do not require and user interaction Flow_07ge8uf - Flow_09m77pd + Flow_0peeyne - - - - - - - - - - - - - Flow_09m77pd - Flow_1r9hwx3 + + + #### Business Rule Task + + + + +This step is internal to the system and do not require and user interaction + Flow_0peeyne + Flow_0tqna2m + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - - + + - - + + + + + diff --git a/crc/static/reference/rrt_documents.xlsx b/crc/static/reference/rrt_documents.xlsx index 4e1663b2b9e397549f6115f57a6fb39736c31229..cb09fd0f5f69f1e616a159cf20eb36b91518e3d4 100644 GIT binary patch delta 2015 zcmV<52O#*=Ox{eeNeh2Z=a&hi0RRAz0{{RN0001ZY%g|&gT9LrRm#G^QZoNt8+JfX+S!y(uP2HE5%=bH0WUmwqp(@dzbVh@m!OHko`EHE7G3a;*(1pZ(yhwfeQi zyFr-VaG8*nNszG_Ohk~0=+|!=1l&8{O)nX}S5o&ukqD?@m@M>(QFZ4Ot+2~tZdEMZTP_0z_CP2;0m7Cu*1;h2F~(ZJgiv+ z*g&vS(P`UVMx+BH5&X?@oskTNN&dyMEq!pN+dSRLOVE5RAu~_HY6JEq8CI14m z0}U_=1&|{3(g>3{7bkz(I1qi`s{IF*ANFa%7cK_YI5ogRQZ-4anOvTQExA4)(Uu% zfnpWKk$Tgl21$a^hfT@_UB!s|V0US79 z{-@Bd#haCS-#fh@Bbn=BSPHVDl1+L1JqpzpQc-jvTho7CtWJ}I_Mqcf9oxYYCHTbm z?^(3j=FuSaq8G3 z7nL96lMk3h6-fMl^)1hQMF|EaHpxA-Uu9u8EvA>Ul>}7gl_X%c3el|`b%G{!Y|zA}b!>mw#AY|JMIEz$p%kCQ6{VQ1l-g&z zF=>Ejn>nq6=UQ)Ii#leXlGDg4zQQY7F?-T_Ru_{7c&@b$o@>2y!@X5o@l?-+*5Vo{KHrRP*heZhQ7TD{s|ROx+a0*wVeN-= zLKJ_8{8bE>8ouMU)pOkH2bbbHyM}Dhb1)naSJ~E_t6zb*Ome-?OzkqlMT9#>w;5w2 z<3b4wz(GJdpr!^ zYq9ZkDqeS##P*q%J`gr;z)_i5t#x+>YIP=ufOxFMl|F*J`=0V^7?vHSAl~zsJrg!Bu?L zC|+%w5*nE75HQ^)4vIF>_mabC`S^`|4w_Aotx9VpFfF+oufA`b+u85ipbS{5^Zj67|KV5F=COwa)|n&!7R8I>um z6}+lif-)SmXO)CbshpsVF*3zi(+yNa2bLi$xT;k}j4jm~OG3ZN8gLwje?we>A&eM; zTR~s;is&fGXfG+Zs&oR3Vkn^kVKk0{2%otIRHgrX;E)Su#mzz5nT~cZwHfWep1s$+ zCpJwJG>Nle{YUtH{<3hR`dqYGQ9yadXll4AATHPyEz3r-fldP5=ZH4YulyN zxdGR$6*}$Vd6*QA`a6yvFD?m_3C|_L)WN|2LDwY_(@nqqx5c+ z4u%(sCjl32NyK_U7Kmk&dDkUHVkRtWc2$N4G6#^_5lR+06lYk{E0cw-0B_kWRx3r0e x0RRB#0ssIJ00000000000000006&xIB|ZfGAOHZ95g`+kJ|++bha&(00040Kz|{Z% delta 2029 zcmV%UNeh3SOW0nV0RRAd0{{RN0001ZY%gZWbR^!;YF8e_$K3n~{g>evsb+8S;6T;Y!(vWP3vmsF-;SkI?Ut z%XdeEfnNAOXv>cyiUJ>k&$ro98>G(z2r{oa(h3aQI{!V$GVj6eu%8sm4cM>r?WiHH zyt5X~7V*(^>$tmRYmOEze=B1{9XnOHfnuavyDVrMa zOd22jpMUZWT)fI1V~T(wl~ZGWtV4vECsURBB2U@#nzOWU^LWDNo+cn zH8c|%rAEOp5%+NL%_a)if(QGArK%tiET#(jmv zO=^%Nn0(r%T+nrlxQ}+91}AR)=Yl?o0>LHUs4nQtLg;18V{>eR$@jtt5#(tnf^DY8 zgFRV)4h+-xbbh`?>>wNOMA7vNi&>yph;1@Rp8hrGuesqTOj%&eXv)a@ONJn{jpEr8 z+o(B6gzpO`1JpvE1EeqDF_y!VBvD?`*hqTRCmE%C;fMva zBgWFv>D_bysvR(v4g;%e-?37^bi`OX_Uvx23jT&hM4*?%SbEyy`#guGC&p8B{kz0} zx}_t=li1#UOX46YcEZXtUphB;`7%(m*i2nh3#zE^34S0rC$Os$m5Ecwp17#|AfJ4| zEUG}_|C?`l<{L^dD6v`Yq5UcgyKOPQv~b!Mi%W}M+v4|2i+= zN}vUvYi)w(T5n*uw;C(fMOSDou7Tq7&FGeWreYhVlEk=raAtSik=q|PemEz8L~+Pp z#c-+NJMLNo$E|*FDXz0?$PPUR!|`yHZNs_xFA$eWuJ?theMY#5aL4F2V`^kvC_y7- zkiBG&k&tQy!!P?}opa#>-og9_hkd-rqN&ldaKoX=Qc=*D0e_+GQH$_B9)=&aSpOHA zWWUYz)A))$L6R@*K%RL*(_f!|*>)dOVQAEL^zh)BwR6r>QvMH~JOZNaSDNf3%4}<7 zNA)C??_nf+f!O}W4QDxS&+a@7XI5vBKb)a!bzIl6XYSk@4=wATQ|FZ6Dn4rzueMDI z4NP_nm~InCMVsh*$#JrJ`bIwU5IqS}`bUapW?Q^}e?e~bEAk3oXw07yTC7<$tNy4l=4?+(O6q3-=n<{M_wX!TE z<@NWK<-|#7p`e3yXEe;tsu=GY5o|yk-pUdsQH%nRv}Ih@C0fiU;T;N$C6W=*N+{7D z3>sJ0R|Qo$Z8f~=R)Mx0OyHEH$*C&Q%376UZ0HIaVj{h5K@lA!8670mS_?10CK+&h2QV;Hb7kFzdhnv*_8^F{Q@4MzB?tDfukHoxf@jdWl?DC{cbn6&;oZio zB=eM^cD8*=y&GVGn>+7QIwEh`@6aWSQ2mrGO zA&&zIolDqWoB;p;dy}6eQvnH+^CUn4OOrGuARAoVWabJ4008j{000;O0000000000 z00000C6jL@MFC}#wIw4Plra=nf&l;k=mG!$5dZ)H00000000000026Z^Cdn3^OG_r LCI)>Y000002Z+6I From 1702ab25767b7ac204472d60771cca9895e9fd1d Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Mon, 1 Jun 2020 00:36:25 -0400 Subject: [PATCH 18/18] Fixes decision tables that were causing failing tests --- crc/static/bpmn/research_rampup/exclusive_area_monitors.dmn | 6 +++--- crc/static/bpmn/research_rampup/shared_area_monitors.dmn | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crc/static/bpmn/research_rampup/exclusive_area_monitors.dmn b/crc/static/bpmn/research_rampup/exclusive_area_monitors.dmn index 236796b3..7a2251de 100644 --- a/crc/static/bpmn/research_rampup/exclusive_area_monitors.dmn +++ b/crc/static/bpmn/research_rampup/exclusive_area_monitors.dmn @@ -1,10 +1,10 @@ - + - len(exclusive) + 'exclusive' in locals() and len(exclusive) @@ -14,7 +14,7 @@ - No exclusvie spaces without Area Monitor + No exclusive spaces without Area Monitor >0 diff --git a/crc/static/bpmn/research_rampup/shared_area_monitors.dmn b/crc/static/bpmn/research_rampup/shared_area_monitors.dmn index db3d43b3..f0746f42 100644 --- a/crc/static/bpmn/research_rampup/shared_area_monitors.dmn +++ b/crc/static/bpmn/research_rampup/shared_area_monitors.dmn @@ -1,10 +1,10 @@ - + - len(shared) + 'shared' in locals() and len(shared)