Merge pull request #91 from sartography/feature/approvals_enhancements

Approvals enhancements
This commit is contained in:
Aaron Louie 2020-06-01 00:46:59 -04:00 committed by GitHub
commit bb32bd7090
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 504 additions and 268 deletions

View File

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

View File

@ -5,15 +5,24 @@ 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:
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
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.')

View File

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

View File

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

View File

@ -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)
return LookupDataSchema(many=True).dump(lookup_data)

View File

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

View File

@ -11,10 +11,11 @@ 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):
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.
@ -67,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
@ -84,11 +85,25 @@ 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:
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)

View File

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

View File

@ -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=[], approvals=[], **argsv):
self.id = id
self.user_uid = user_uid
self.title = title
@ -117,8 +118,9 @@ class Study(object):
self.hsr_number = hsr_number
self.ind_number = ind_number
self.categories = categories
self.approvals = approvals
self.warnings = []
self.files = []
self.files = files
@classmethod
def from_model(cls, study_model: StudyModel):
@ -149,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(SimpleFileSchema), dump_only=True)
files = fields.List(fields.Nested(FileSchema), 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"]
"sponsor", "ind_number", "approvals", "files"]
unknown = INCLUDE
@marshmallow.post_load

View File

@ -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"""
@ -78,7 +84,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)

View File

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

View File

@ -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
@ -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 Approval
class StudyService(object):
@ -54,7 +56,13 @@ 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]
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.

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" id="Definitions_06veek1" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<decision id="Decision_ExclusiveAMCheck" name="Exclusive AM Check">
<decisionTable id="decisionTable_1">
<input id="InputClause_1z0jy2o" label="How Many Exclusive Spaces?">
<inputExpression id="LiteralExpression_0tvij2j" typeRef="integer" expressionLanguage="python">
<text>'exclusive' in locals() and len(exclusive)</text>
</inputExpression>
</input>
<input id="input_1" label="Number Without Area Monitor">
<inputExpression id="inputExpression_1" typeRef="integer" expressionLanguage="python">
<text>sum([1 for x in exclusive if x.get('ExclusiveSpaceAMComputingID',None) == None])</text>
</inputExpression>
</input>
<output id="output_1" label="All Possible Area Monitors Entered" name="isAllExclusiveAreaMonitors" typeRef="boolean" />
<rule id="DecisionRule_07162mr">
<description>No exclusive spaces without Area Monitor</description>
<inputEntry id="UnaryTests_1892rx8">
<text>&gt;0</text>
</inputEntry>
<inputEntry id="UnaryTests_1jqxc3u">
<text>0</text>
</inputEntry>
<outputEntry id="LiteralExpression_16l50ps">
<text>true</text>
</outputEntry>
</rule>
<rule id="DecisionRule_0ifa4wu">
<description>One or more exclusive space without an Area Monitor</description>
<inputEntry id="UnaryTests_1jakyab">
<text>&gt;0</text>
</inputEntry>
<inputEntry id="UnaryTests_0szbwxc">
<text>&gt; 0</text>
</inputEntry>
<outputEntry id="LiteralExpression_0td8sa6">
<text>false</text>
</outputEntry>
</rule>
<rule id="DecisionRule_026r0im">
<description>No exclusive spaces entered</description>
<inputEntry id="UnaryTests_0c670b6">
<text>0</text>
</inputEntry>
<inputEntry id="UnaryTests_0j06ysc">
<text></text>
</inputEntry>
<outputEntry id="LiteralExpression_1apwzvv">
<text>true</text>
</outputEntry>
</rule>
</decisionTable>
</decision>
</definitions>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1oogn9j" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.3">
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1oogn9j" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.5.0">
<bpmn:process id="Process_0ssahs9" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_05ja25w</bpmn:outgoing>
@ -159,7 +159,7 @@ Enter the following information for the PI submitting this request</bpmn:documen
</bpmn:userTask>
<bpmn:sequenceFlow id="SequenceFlow_0h50bp3" sourceRef="ManualTask_Instructions" targetRef="Activity-PI_Info" />
<bpmn:sequenceFlow id="SequenceFlow_05ja25w" sourceRef="StartEvent_1" targetRef="ManualTask_Instructions" />
<bpmn:userTask id="Personnel" name="Enter Personnel" camunda:formKey="Personnel">
<bpmn:userTask id="Activity_Personnel" name="Enter Personnel" camunda:formKey="Research Personnel">
<bpmn:documentation>#### 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).
@ -226,8 +226,8 @@ Provide information on all personnel you are requesting approval for reentry int
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1eiud85</bpmn:incoming>
<bpmn:outgoing>Flow_1nbjr72</bpmn:outgoing>
<bpmn:incoming>Flow_0hc1r8a</bpmn:incoming>
<bpmn:outgoing>Flow_1yxaewj</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="UserTask_CoreResource" name="Enter Core Resources" camunda:formKey="Core Resources">
<bpmn:documentation>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)</bpmn:documentation>
@ -249,8 +249,8 @@ Provide information on all personnel you are requesting approval for reentry int
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_15zy1q7</bpmn:incoming>
<bpmn:outgoing>Flow_12ie6w0</bpmn:outgoing>
<bpmn:incoming>Flow_1n69wsr</bpmn:incoming>
<bpmn:outgoing>Flow_13pusfu</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="EndEvent_09wp7av">
<bpmn:documentation>#### End of Research Ramp-up Plan Workflow
@ -288,6 +288,9 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a
<camunda:property id="description" value="Enter the room number or other unique identifier" />
<camunda:property id="hide_expression" value="model.SharedSpaceBuilding === &#34;Other&#34;" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="ShareSpaceRoomIDBuilding" label="Room No, and Building Name" type="string">
<camunda:properties>
@ -342,28 +345,10 @@ When your Research Ramp-up Plan is complete and ready to submit for review and a
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_19xeq76</bpmn:incoming>
<bpmn:outgoing>Flow_16342pm</bpmn:outgoing>
<bpmn:incoming>Flow_0o4tg9g</bpmn:incoming>
<bpmn:outgoing>Flow_1n69wsr</bpmn:outgoing>
</bpmn:userTask>
<bpmn:parallelGateway id="Gateway_0frfdnc">
<bpmn:incoming>Flow_1v7r1tg</bpmn:incoming>
<bpmn:outgoing>Flow_19xeq76</bpmn:outgoing>
<bpmn:outgoing>Flow_0qf2y84</bpmn:outgoing>
<bpmn:outgoing>Flow_15zy1q7</bpmn:outgoing>
<bpmn:outgoing>Flow_0ya8hw8</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:parallelGateway id="Gateway_1vj4zd3">
<bpmn:incoming>Flow_0tk64b6</bpmn:incoming>
<bpmn:incoming>Flow_12ie6w0</bpmn:incoming>
<bpmn:incoming>Flow_0zz2hbq</bpmn:incoming>
<bpmn:incoming>Flow_16342pm</bpmn:incoming>
<bpmn:outgoing>Flow_1eiud85</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_19xeq76" sourceRef="Gateway_0frfdnc" targetRef="Activity_SharedSpaceInfo" />
<bpmn:sequenceFlow id="Flow_16342pm" sourceRef="Activity_SharedSpaceInfo" targetRef="Gateway_1vj4zd3" />
<bpmn:sequenceFlow id="Flow_16y8glw" sourceRef="Activity-PI_Info" targetRef="Activity_1u58hox" />
<bpmn:sequenceFlow id="Flow_0qf2y84" sourceRef="Gateway_0frfdnc" targetRef="Activity_ExclusiveSpace" />
<bpmn:sequenceFlow id="Flow_0tk64b6" sourceRef="Activity_ExclusiveSpace" targetRef="Gateway_1vj4zd3" />
<bpmn:userTask id="Activity_ExclusiveSpace" name="Enter Exclusive Space" camunda:formKey="ExclusiveSpace">
<bpmn:documentation>#### Space managed exclusively by {{ PIComputingID.label }}
@ -389,6 +374,9 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp
<camunda:property id="repeat" value="Exclusive" />
<camunda:property id="description" value="Enter the room number or other unique identifier" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="ExclusiveSpaceRoomIDBuilding" label="Room No, and Building Name" type="string">
<camunda:properties>
@ -448,12 +436,9 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0qf2y84</bpmn:incoming>
<bpmn:outgoing>Flow_0tk64b6</bpmn:outgoing>
<bpmn:incoming>Flow_0uc4o6c</bpmn:incoming>
<bpmn:outgoing>Flow_0o4tg9g</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_15zy1q7" sourceRef="Gateway_0frfdnc" targetRef="UserTask_CoreResource" />
<bpmn:sequenceFlow id="Flow_12ie6w0" sourceRef="UserTask_CoreResource" targetRef="Gateway_1vj4zd3" />
<bpmn:sequenceFlow id="Flow_0ya8hw8" sourceRef="Gateway_0frfdnc" targetRef="Activity_nonUVASpaces" />
<bpmn:userTask id="Activity_nonUVASpaces" name="Enter non-UVA Spaces" camunda:formKey="nonUVA Spaces">
<bpmn:extensionElements>
<camunda:formData>
@ -476,12 +461,9 @@ Submit one entry for each space the PI is the exclusive investigator. If all sp
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0ya8hw8</bpmn:incoming>
<bpmn:outgoing>Flow_0zz2hbq</bpmn:outgoing>
<bpmn:incoming>Flow_13pusfu</bpmn:incoming>
<bpmn:outgoing>Flow_0hc1r8a</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_0zz2hbq" sourceRef="Activity_nonUVASpaces" targetRef="Gateway_1vj4zd3" />
<bpmn:sequenceFlow id="Flow_1eiud85" sourceRef="Gateway_1vj4zd3" targetRef="Personnel" />
<bpmn:sequenceFlow id="Flow_1nbjr72" sourceRef="Personnel" targetRef="PersonnelWeeklyScheduleTask" />
<bpmn:userTask id="Activity_DistanceReq" name="Enter Distancing Requirements" camunda:formKey="Distancing Requirements">
<bpmn:documentation>#### 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.</bpmn:documentation>
@ -499,18 +481,9 @@ Maintain social distancing by designing space between people to be at least 9 fe
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0p2r1bo</bpmn:incoming>
<bpmn:outgoing>Flow_0tz5c2v</bpmn:outgoing>
<bpmn:incoming>Flow_1itd8db</bpmn:incoming>
<bpmn:outgoing>Flow_1lo964l</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_0p2r1bo" sourceRef="Gateway_18jn18b" targetRef="Activity_DistanceReq" />
<bpmn:parallelGateway id="Gateway_18jn18b">
<bpmn:incoming>Flow_097fpi3</bpmn:incoming>
<bpmn:outgoing>Flow_0p2r1bo</bpmn:outgoing>
<bpmn:outgoing>Flow_0mkh1wn</bpmn:outgoing>
<bpmn:outgoing>Flow_1yqkpgu</bpmn:outgoing>
<bpmn:outgoing>Flow_1c6m5wv</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_0mkh1wn" sourceRef="Gateway_18jn18b" targetRef="Activity_PWA" />
<bpmn:userTask id="Activity_PWA" name="Enter Physical Work Arrangements" camunda:formKey="Physical Work Arrangements">
<bpmn:documentation>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.
@ -533,10 +506,9 @@ Maintain social distancing by designing space between people to be at least 9 fe
<camunda:formField id="PWAFiles" label="Upload supporting files" type="files" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0mkh1wn</bpmn:incoming>
<bpmn:outgoing>Flow_0zrsh65</bpmn:outgoing>
<bpmn:incoming>Flow_1lo964l</bpmn:incoming>
<bpmn:outgoing>Flow_0wgdxa6</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1yqkpgu" sourceRef="Gateway_18jn18b" targetRef="Activity_HSR" />
<bpmn:userTask id="Activity_HSR" name="Enter Health Safety Requirements" camunda:formKey="Lab Safety Plan">
<bpmn:documentation>#### 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&amp;source=gmail&amp;ust=1590687968958000&amp;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:
@ -551,10 +523,9 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur
<camunda:formField id="LabSafetyPlan" label="Upload Lab Safety Plan" type="files" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1yqkpgu</bpmn:incoming>
<bpmn:outgoing>Flow_1ox5nv6</bpmn:outgoing>
<bpmn:incoming>Flow_0wgdxa6</bpmn:incoming>
<bpmn:outgoing>Flow_0judgmp</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1c6m5wv" sourceRef="Gateway_18jn18b" targetRef="Activity_OtherReq" />
<bpmn:userTask id="Activity_OtherReq" name="Enter Other Requirements" camunda:formKey="Other Requirements">
<bpmn:extensionElements>
<camunda:formData>
@ -598,8 +569,8 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1c6m5wv</bpmn:incoming>
<bpmn:outgoing>Flow_0qbi47d</bpmn:outgoing>
<bpmn:incoming>Flow_0judgmp</bpmn:incoming>
<bpmn:outgoing>Flow_11uqavk</bpmn:outgoing>
</bpmn:userTask>
<bpmn:manualTask id="Activity_SubmitPlan" name="Acknowledge Plan Submission">
<bpmn:documentation>#### By submitting this request, you understand that every member listed in this form for on Grounds laboratory access will:
@ -609,35 +580,28 @@ Use the EHS [Lab Safety Plan During COVID 19 template](https://www.google.com/ur
<bpmn:incoming>Flow_08njvvi</bpmn:incoming>
<bpmn:outgoing>Flow_0j4rs82</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:sequenceFlow id="Flow_0zrsh65" sourceRef="Activity_PWA" targetRef="Gateway_0sijkgx" />
<bpmn:sequenceFlow id="Flow_0tz5c2v" sourceRef="Activity_DistanceReq" targetRef="Gateway_0sijkgx" />
<bpmn:sequenceFlow id="Flow_1ox5nv6" sourceRef="Activity_HSR" targetRef="Gateway_0sijkgx" />
<bpmn:sequenceFlow id="Flow_0qbi47d" sourceRef="Activity_OtherReq" targetRef="Gateway_0sijkgx" />
<bpmn:sequenceFlow id="Flow_06873ag" sourceRef="Gateway_0sijkgx" targetRef="Activity_GenerateRRP" />
<bpmn:parallelGateway id="Gateway_0sijkgx">
<bpmn:incoming>Flow_0zrsh65</bpmn:incoming>
<bpmn:incoming>Flow_0tz5c2v</bpmn:incoming>
<bpmn:incoming>Flow_1ox5nv6</bpmn:incoming>
<bpmn:incoming>Flow_0qbi47d</bpmn:incoming>
<bpmn:outgoing>Flow_06873ag</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:scriptTask id="Activity_GenerateRRP" name="Generate RRP">
<bpmn:documentation>#### Script Task
This step is internal to the system and do not require and user interaction</bpmn:documentation>
<bpmn:incoming>Flow_06873ag</bpmn:incoming>
<bpmn:incoming>Flow_11uqavk</bpmn:incoming>
<bpmn:outgoing>Flow_0aqgwvu</bpmn:outgoing>
<bpmn:script>CompleteTemplate ResearchRampUpPlan.docx RESEARCH_RAMPUP</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_0aqgwvu" sourceRef="Activity_GenerateRRP" targetRef="Activity_AcknowledgePlanReview" />
<bpmn:sequenceFlow id="Flow_0j4rs82" sourceRef="Activity_SubmitPlan" targetRef="Activity_0absozl" />
<bpmn:sequenceFlow id="Flow_07ge8uf" sourceRef="Activity_0absozl" targetRef="Activity_RequestStatus" />
<bpmn:sequenceFlow id="Flow_1ufh44h" sourceRef="Activity_RequestStatus" targetRef="Activity_AreaMonitorNotification" />
<bpmn:sequenceFlow id="Flow_07ge8uf" sourceRef="Activity_0absozl" targetRef="Activity_1sq56an" />
<bpmn:userTask id="Activity_RequestStatus" name="Check-on Request Status" camunda:formKey="Rquest Status">
<bpmn:documentation>#### 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.</bpmn:documentation>
<bpmn:extensionElements>
@ -646,24 +610,94 @@ If a rejection notification is received, go back to the first step that needs to
<camunda:properties>
<camunda:property id="enum_type" value="checkbox" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
<camunda:value id="ApprovalNotificationReceived" name="Approval Notification Received?" />
</camunda:formField>
<camunda:formField id="RequiredTraining" label="Please Confirm:" type="enum">
<camunda:properties>
<camunda:property id="enum_type" value="checkbox" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
<camunda:value id="AllRequiredTraining" name="All Required Training Completed?" />
</camunda:formField>
<camunda:formField id="NeededSupplies" label="Please Confirm&#34;" type="enum">
<camunda:properties>
<camunda:property id="enum_type" value="checkbox" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
<camunda:value id="NeededSupplies" name="All Needed Supplies Secured?" />
</camunda:formField>
<camunda:formField id="ExclusiveSpaceBuilding" label="Room No. &#38; Building Name" type="autocomplete">
<camunda:properties>
<camunda:property id="repeat" value="Exclusive" />
<camunda:property id="read_only" value="true" />
<camunda:property id="spreadsheet.name" value="Buildinglist.xls" />
<camunda:property id="spreadsheet.value.column" value="Value" />
<camunda:property id="spreadsheet.label.column" value="Building Name" />
<camunda:property id="repeat_title" value="Any missing Area Monitors for Exclusive Spaces must be entered" />
<camunda:property id="repeat_hide_expression" value="model.isAllExclusiveAreaMonitors" />
</camunda:properties>
</camunda:formField>
<camunda:formField id="ExclusiveSpaceRoomID" label="Exclusive Space Room ID" type="string">
<camunda:properties>
<camunda:property id="repeat" value="Exclusive" />
<camunda:property id="read_only" value="true" />
</camunda:properties>
</camunda:formField>
<camunda:formField id="ExclusiveSpaceType" label="Space Room Type" type="enum">
<camunda:properties>
<camunda:property id="read_only" value="true" />
<camunda:property id="repeat" value="Exclusive" />
</camunda:properties>
<camunda:value id="Lab" name="Lab" />
<camunda:value id="Office" name="Office" />
</camunda:formField>
<camunda:formField id="ExclusiveSpaceAMComputingID" label="Area Monitor" type="autocomplete">
<camunda:properties>
<camunda:property id="ldap.lookup" value="true" />
<camunda:property id="placeholder" value="wxy0z or Smith" />
<camunda:property id="repeat" value="Exclusive" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="SharedSpaceBuilding" label="Building Name" type="autocomplete">
<camunda:properties>
<camunda:property id="spreadsheet.name" value="Buildinglist.xls" />
<camunda:property id="spreadsheet.value.column" value="Value" />
<camunda:property id="spreadsheet.label.column" value="Building Name" />
<camunda:property id="repeat" value="Shared" />
<camunda:property id="repeat_title" value="Any missing Area Monitors for Shared Spaces must be entered" />
<camunda:property id="read_only" value="true" />
<camunda:property id="repeat_hide_expression" value="model.isAllSharedAreaMonitors" />
</camunda:properties>
</camunda:formField>
<camunda:formField id="SharedSpaceRoomID" label="Shared Space Room ID" type="string">
<camunda:properties>
<camunda:property id="read_only" value="true" />
<camunda:property id="repeat" value="Shared" />
</camunda:properties>
</camunda:formField>
<camunda:formField id="SharedSpaceAMComputingID" label="Area Monitor" type="autocomplete">
<camunda:properties>
<camunda:property id="ldap.lookup" value="true" />
<camunda:property id="repeat" value="Shared" />
<camunda:property id="placeholder" value="wxy0z or Smith" />
</camunda:properties>
<camunda:validation>
<camunda:constraint name="required" config="true" />
</camunda:validation>
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_07ge8uf</bpmn:incoming>
<bpmn:outgoing>Flow_1ufh44h</bpmn:outgoing>
<bpmn:outgoing>SequenceFlow_0qc39tw</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_05w8yd6" sourceRef="Activity_WhatNext" targetRef="EndEvent_09wp7av" />
<bpmn:sequenceFlow id="Flow_08njvvi" sourceRef="Activity_ApprovalInfo" targetRef="Activity_SubmitPlan" />
@ -698,7 +732,7 @@ Notify the Area Monitor for
#### 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 %}</bpmn:documentation>
<bpmn:incoming>Flow_1ufh44h</bpmn:incoming>
<bpmn:incoming>SequenceFlow_0qc39tw</bpmn:incoming>
<bpmn:outgoing>Flow_0cpmvcw</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:scriptTask id="Activity_0absozl" name="Execute Plan Submission">
@ -710,18 +744,16 @@ This step is internal to the system and do not require and user interaction</bpm
<bpmn:outgoing>Flow_07ge8uf</bpmn:outgoing>
<bpmn:script>RequestApproval ApprvlApprvr1 ApprvlApprvr2</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_1v7r1tg" sourceRef="Activity_1u58hox" targetRef="Gateway_0frfdnc" />
<bpmn:scriptTask id="Activity_1u58hox" name="Update Request">
<bpmn:documentation>#### Script Task
This step is internal to the system and do not require and user interaction</bpmn:documentation>
<bpmn:incoming>Flow_16y8glw</bpmn:incoming>
<bpmn:outgoing>Flow_1v7r1tg</bpmn:outgoing>
<bpmn:outgoing>Flow_0uc4o6c</bpmn:outgoing>
<bpmn:script>UpdateStudy title:PIComputingID.label pi:PIComputingID.value</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_097fpi3" sourceRef="PersonnelWeeklyScheduleTask" targetRef="Gateway_18jn18b" />
<bpmn:userTask id="PersonnelWeeklyScheduleTask" name="Upload Weekly Personnel Schedule(s)" camunda:formKey="Personnel Schedule">
<bpmn:userTask id="PersonnelSchedule" name="Upload Weekly Personnel Schedule(s)" camunda:formKey="Personnel Weekly Schedule">
<bpmn:documentation>#### 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.
@ -741,236 +773,216 @@ Provide initial weekly schedule(s) for the PI and all personnel for whom access
</camunda:formField>
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1nbjr72</bpmn:incoming>
<bpmn:outgoing>Flow_097fpi3</bpmn:outgoing>
<bpmn:incoming>Flow_1yxaewj</bpmn:incoming>
<bpmn:outgoing>Flow_1itd8db</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_0uc4o6c" sourceRef="Activity_1u58hox" targetRef="Activity_ExclusiveSpace" />
<bpmn:sequenceFlow id="Flow_0o4tg9g" sourceRef="Activity_ExclusiveSpace" targetRef="Activity_SharedSpaceInfo" />
<bpmn:sequenceFlow id="Flow_1n69wsr" sourceRef="Activity_SharedSpaceInfo" targetRef="UserTask_CoreResource" />
<bpmn:sequenceFlow id="Flow_13pusfu" sourceRef="UserTask_CoreResource" targetRef="Activity_nonUVASpaces" />
<bpmn:sequenceFlow id="Flow_1yxaewj" sourceRef="Activity_Personnel" targetRef="PersonnelSchedule" />
<bpmn:sequenceFlow id="Flow_1itd8db" sourceRef="PersonnelSchedule" targetRef="Activity_DistanceReq" />
<bpmn:sequenceFlow id="Flow_1lo964l" sourceRef="Activity_DistanceReq" targetRef="Activity_PWA" />
<bpmn:sequenceFlow id="Flow_0wgdxa6" sourceRef="Activity_PWA" targetRef="Activity_HSR" />
<bpmn:sequenceFlow id="Flow_0judgmp" sourceRef="Activity_HSR" targetRef="Activity_OtherReq" />
<bpmn:sequenceFlow id="Flow_11uqavk" sourceRef="Activity_OtherReq" targetRef="Activity_GenerateRRP" />
<bpmn:sequenceFlow id="Flow_0peeyne" sourceRef="Activity_1sq56an" targetRef="Activity_14xt8is" />
<bpmn:businessRuleTask id="Activity_1sq56an" name="Check Exclusive Area Monitors" camunda:decisionRef="Decision_ExclusiveAMCheck">
<bpmn:documentation>#### Business Rule Task
This step is internal to the system and do not require and user interaction</bpmn:documentation>
<bpmn:incoming>Flow_07ge8uf</bpmn:incoming>
<bpmn:outgoing>Flow_0peeyne</bpmn:outgoing>
</bpmn:businessRuleTask>
<bpmn:sequenceFlow id="Flow_0tqna2m" sourceRef="Activity_14xt8is" targetRef="Activity_RequestStatus" />
<bpmn:businessRuleTask id="Activity_14xt8is" name="Check Shared Area Monitors" camunda:decisionRef="Decision_SharedAMCheck">
<bpmn:documentation>#### Business Rule Task
This step is internal to the system and do not require and user interaction</bpmn:documentation>
<bpmn:incoming>Flow_0peeyne</bpmn:incoming>
<bpmn:outgoing>Flow_0tqna2m</bpmn:outgoing>
</bpmn:businessRuleTask>
<bpmn:sequenceFlow id="Flow_0hc1r8a" sourceRef="Activity_nonUVASpaces" targetRef="Activity_Personnel" />
<bpmn:sequenceFlow id="SequenceFlow_0qc39tw" sourceRef="Activity_RequestStatus" targetRef="Activity_AreaMonitorNotification" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0ssahs9">
<bpmndi:BPMNEdge id="Flow_097fpi3_di" bpmnElement="Flow_097fpi3">
<di:waypoint x="1290" y="307" />
<di:waypoint x="1335" y="307" />
<bpmndi:BPMNEdge id="SequenceFlow_0qc39tw_di" bpmnElement="SequenceFlow_0qc39tw">
<di:waypoint x="3290" y="117" />
<di:waypoint x="3330" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1v7r1tg_di" bpmnElement="Flow_1v7r1tg">
<di:waypoint x="630" y="307" />
<di:waypoint x="685" y="307" />
<bpmndi:BPMNEdge id="Flow_0hc1r8a_di" bpmnElement="Flow_0hc1r8a">
<di:waypoint x="1260" y="117" />
<di:waypoint x="1310" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0tqna2m_di" bpmnElement="Flow_0tqna2m">
<di:waypoint x="3140" y="117" />
<di:waypoint x="3190" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0peeyne_di" bpmnElement="Flow_0peeyne">
<di:waypoint x="2990" y="117" />
<di:waypoint x="3040" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_11uqavk_di" bpmnElement="Flow_11uqavk">
<di:waypoint x="2120" y="117" />
<di:waypoint x="2170" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0judgmp_di" bpmnElement="Flow_0judgmp">
<di:waypoint x="1980" y="117" />
<di:waypoint x="2020" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0wgdxa6_di" bpmnElement="Flow_0wgdxa6">
<di:waypoint x="1840" y="117" />
<di:waypoint x="1880" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1lo964l_di" bpmnElement="Flow_1lo964l">
<di:waypoint x="1700" y="117" />
<di:waypoint x="1740" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1itd8db_di" bpmnElement="Flow_1itd8db">
<di:waypoint x="1550" y="117" />
<di:waypoint x="1600" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1yxaewj_di" bpmnElement="Flow_1yxaewj">
<di:waypoint x="1410" y="117" />
<di:waypoint x="1450" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_13pusfu_di" bpmnElement="Flow_13pusfu">
<di:waypoint x="1100" y="117" />
<di:waypoint x="1160" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1n69wsr_di" bpmnElement="Flow_1n69wsr">
<di:waypoint x="950" y="117" />
<di:waypoint x="1000" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0o4tg9g_di" bpmnElement="Flow_0o4tg9g">
<di:waypoint x="790" y="117" />
<di:waypoint x="850" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0uc4o6c_di" bpmnElement="Flow_0uc4o6c">
<di:waypoint x="630" y="117" />
<di:waypoint x="690" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0cpmvcw_di" bpmnElement="Flow_0cpmvcw">
<di:waypoint x="2600" y="307" />
<di:waypoint x="2650" y="307" />
<di:waypoint x="3430" y="117" />
<di:waypoint x="3480" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_08njvvi_di" bpmnElement="Flow_08njvvi">
<di:waypoint x="2030" y="307" />
<di:waypoint x="2060" y="307" />
<di:waypoint x="2560" y="117" />
<di:waypoint x="2610" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_05w8yd6_di" bpmnElement="Flow_05w8yd6">
<di:waypoint x="2750" y="307" />
<di:waypoint x="2822" y="307" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1ufh44h_di" bpmnElement="Flow_1ufh44h">
<di:waypoint x="2460" y="307" />
<di:waypoint x="2500" y="307" />
<di:waypoint x="3580" y="117" />
<di:waypoint x="3652" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_07ge8uf_di" bpmnElement="Flow_07ge8uf">
<di:waypoint x="2310" y="307" />
<di:waypoint x="2360" y="307" />
<di:waypoint x="2850" y="117" />
<di:waypoint x="2890" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0j4rs82_di" bpmnElement="Flow_0j4rs82">
<di:waypoint x="2160" y="307" />
<di:waypoint x="2210" y="307" />
<di:waypoint x="2710" y="117" />
<di:waypoint x="2750" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0aqgwvu_di" bpmnElement="Flow_0aqgwvu">
<di:waypoint x="1770" y="307" />
<di:waypoint x="1800" y="307" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_06873ag_di" bpmnElement="Flow_06873ag">
<di:waypoint x="1625" y="307" />
<di:waypoint x="1670" y="307" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0qbi47d_di" bpmnElement="Flow_0qbi47d">
<di:waypoint x="1530" y="510" />
<di:waypoint x="1600" y="510" />
<di:waypoint x="1600" y="332" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1ox5nv6_di" bpmnElement="Flow_1ox5nv6">
<di:waypoint x="1530" y="380" />
<di:waypoint x="1600" y="380" />
<di:waypoint x="1600" y="332" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0tz5c2v_di" bpmnElement="Flow_0tz5c2v">
<di:waypoint x="1530" y="120" />
<di:waypoint x="1600" y="120" />
<di:waypoint x="1600" y="282" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0zrsh65_di" bpmnElement="Flow_0zrsh65">
<di:waypoint x="1530" y="240" />
<di:waypoint x="1600" y="240" />
<di:waypoint x="1600" y="282" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1c6m5wv_di" bpmnElement="Flow_1c6m5wv">
<di:waypoint x="1360" y="332" />
<di:waypoint x="1360" y="510" />
<di:waypoint x="1430" y="510" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1yqkpgu_di" bpmnElement="Flow_1yqkpgu">
<di:waypoint x="1360" y="332" />
<di:waypoint x="1360" y="380" />
<di:waypoint x="1430" y="380" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0mkh1wn_di" bpmnElement="Flow_0mkh1wn">
<di:waypoint x="1360" y="282" />
<di:waypoint x="1360" y="240" />
<di:waypoint x="1430" y="240" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0p2r1bo_di" bpmnElement="Flow_0p2r1bo">
<di:waypoint x="1360" y="282" />
<di:waypoint x="1360" y="120" />
<di:waypoint x="1430" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1nbjr72_di" bpmnElement="Flow_1nbjr72">
<di:waypoint x="1150" y="307" />
<di:waypoint x="1190" y="307" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1eiud85_di" bpmnElement="Flow_1eiud85">
<di:waypoint x="995" y="307" />
<di:waypoint x="1050" y="307" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0zz2hbq_di" bpmnElement="Flow_0zz2hbq">
<di:waypoint x="890" y="510" />
<di:waypoint x="970" y="510" />
<di:waypoint x="970" y="332" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0ya8hw8_di" bpmnElement="Flow_0ya8hw8">
<di:waypoint x="710" y="332" />
<di:waypoint x="710" y="510" />
<di:waypoint x="790" y="510" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_12ie6w0_di" bpmnElement="Flow_12ie6w0">
<di:waypoint x="890" y="370" />
<di:waypoint x="970" y="370" />
<di:waypoint x="970" y="332" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_15zy1q7_di" bpmnElement="Flow_15zy1q7">
<di:waypoint x="710" y="332" />
<di:waypoint x="710" y="370" />
<di:waypoint x="790" y="370" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0tk64b6_di" bpmnElement="Flow_0tk64b6">
<di:waypoint x="890" y="110" />
<di:waypoint x="970" y="110" />
<di:waypoint x="970" y="282" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0qf2y84_di" bpmnElement="Flow_0qf2y84">
<di:waypoint x="710" y="282" />
<di:waypoint x="710" y="110" />
<di:waypoint x="790" y="110" />
<di:waypoint x="2270" y="117" />
<di:waypoint x="2310" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_16y8glw_di" bpmnElement="Flow_16y8glw">
<di:waypoint x="480" y="307" />
<di:waypoint x="530" y="307" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_16342pm_di" bpmnElement="Flow_16342pm">
<di:waypoint x="890" y="240" />
<di:waypoint x="970" y="240" />
<di:waypoint x="970" y="282" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_19xeq76_di" bpmnElement="Flow_19xeq76">
<di:waypoint x="710" y="282" />
<di:waypoint x="710" y="240" />
<di:waypoint x="790" y="240" />
<di:waypoint x="480" y="117" />
<di:waypoint x="530" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1e2qi9s_di" bpmnElement="Flow_1e2qi9s">
<di:waypoint x="1900" y="307" />
<di:waypoint x="1930" y="307" />
<di:waypoint x="2410" y="117" />
<di:waypoint x="2460" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_05ja25w_di" bpmnElement="SequenceFlow_05ja25w">
<di:waypoint x="168" y="307" />
<di:waypoint x="230" y="307" />
<di:waypoint x="168" y="117" />
<di:waypoint x="230" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_0h50bp3_di" bpmnElement="SequenceFlow_0h50bp3">
<di:waypoint x="330" y="307" />
<di:waypoint x="380" y="307" />
<di:waypoint x="330" y="117" />
<di:waypoint x="380" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="132" y="289" width="36" height="36" />
<dc:Bounds x="132" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ManualTask_1ofy9yz_di" bpmnElement="ManualTask_Instructions">
<dc:Bounds x="230" y="267" width="100" height="80" />
<dc:Bounds x="230" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_0xdpoxl_di" bpmnElement="Activity-PI_Info">
<dc:Bounds x="380" y="267" width="100" height="80" />
<dc:Bounds x="380" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_0ecab9j_di" bpmnElement="Personnel">
<dc:Bounds x="1050" y="267" width="100" height="80" />
<bpmndi:BPMNShape id="UserTask_0ecab9j_di" bpmnElement="Activity_Personnel">
<dc:Bounds x="1310" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_0l8vxty_di" bpmnElement="UserTask_CoreResource">
<dc:Bounds x="790" y="330" width="100" height="80" />
<dc:Bounds x="1000" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_09wp7av_di" bpmnElement="EndEvent_09wp7av">
<dc:Bounds x="2822" y="289" width="36" height="36" />
<dc:Bounds x="3652" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1mg5lp9_di" bpmnElement="Activity_AcknowledgePlanReview">
<dc:Bounds x="1800" y="267" width="100" height="80" />
<dc:Bounds x="2310" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1xgrlzr_di" bpmnElement="Activity_SharedSpaceInfo">
<dc:Bounds x="790" y="200" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0tn2il3_di" bpmnElement="Gateway_0frfdnc">
<dc:Bounds x="685" y="282" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_1o1fcbg_di" bpmnElement="Gateway_1vj4zd3">
<dc:Bounds x="945" y="282" width="50" height="50" />
<dc:Bounds x="850" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1jefdme_di" bpmnElement="Activity_ExclusiveSpace">
<dc:Bounds x="790" y="70" width="100" height="80" />
<dc:Bounds x="690" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ysw6zo_di" bpmnElement="Activity_nonUVASpaces">
<dc:Bounds x="790" y="470" width="100" height="80" />
<dc:Bounds x="1160" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1xag6qb_di" bpmnElement="Activity_DistanceReq">
<dc:Bounds x="1430" y="80" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0yof76x_di" bpmnElement="Gateway_18jn18b">
<dc:Bounds x="1335" y="282" width="50" height="50" />
<dc:Bounds x="1600" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_166b0qq_di" bpmnElement="Activity_PWA">
<dc:Bounds x="1430" y="200" width="100" height="80" />
<dc:Bounds x="1740" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0byj9mp_di" bpmnElement="Activity_HSR">
<dc:Bounds x="1430" y="340" width="100" height="80" />
<dc:Bounds x="1880" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0j0p13i_di" bpmnElement="Activity_OtherReq">
<dc:Bounds x="1430" y="470" width="100" height="80" />
<dc:Bounds x="2020" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1h30fo8_di" bpmnElement="Activity_SubmitPlan">
<dc:Bounds x="2060" y="267" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_03lxnzh_di" bpmnElement="Gateway_0sijkgx">
<dc:Bounds x="1575" y="282" width="50" height="50" />
<dc:Bounds x="2610" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_117owwi_di" bpmnElement="Activity_GenerateRRP">
<dc:Bounds x="1670" y="267" width="100" height="80" />
<dc:Bounds x="2170" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0fxf44t_di" bpmnElement="Activity_RequestStatus">
<dc:Bounds x="2360" y="267" width="100" height="80" />
<dc:Bounds x="3190" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_080o38p_di" bpmnElement="Activity_ApprovalInfo">
<dc:Bounds x="1930" y="267" width="100" height="80" />
<dc:Bounds x="2460" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0wuukfn_di" bpmnElement="Activity_WhatNext">
<dc:Bounds x="2650" y="267" width="100" height="80" />
<dc:Bounds x="3480" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0js9ww9_di" bpmnElement="Activity_AreaMonitorNotification">
<dc:Bounds x="2500" y="267" width="100" height="80" />
<dc:Bounds x="3330" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0wnn9de_di" bpmnElement="Activity_0absozl">
<dc:Bounds x="2210" y="267" width="100" height="80" />
<dc:Bounds x="2750" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0f0ak6p_di" bpmnElement="Activity_1u58hox">
<dc:Bounds x="530" y="267" width="100" height="80" />
<dc:Bounds x="530" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_11iexzh_di" bpmnElement="PersonnelWeeklyScheduleTask">
<dc:Bounds x="1190" y="267" width="100" height="80" />
<bpmndi:BPMNShape id="Activity_11iexzh_di" bpmnElement="PersonnelSchedule">
<dc:Bounds x="1450" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0r358ps_di" bpmnElement="Activity_1sq56an">
<dc:Bounds x="2890" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1i34vt6_di" bpmnElement="Activity_14xt8is">
<dc:Bounds x="3040" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" id="Definitions_06veek1" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<decision id="Decision_SharedAMCheck" name="Shared AM Check">
<decisionTable id="decisionTable_1">
<input id="InputClause_1koybx6" label="How Many Shared Spaces">
<inputExpression id="LiteralExpression_1mjo0y4" typeRef="integer" expressionLanguage="python">
<text>'shared' in locals() and len(shared)</text>
</inputExpression>
</input>
<input id="input_1" label="Number Without Area Monitor">
<inputExpression id="inputExpression_1" typeRef="integer" expressionLanguage="python">
<text>sum([1 for x in exclusive if x.get('SharedSpaceAMComputingID',None) == None])</text>
</inputExpression>
</input>
<output id="output_1" label="All Possible Shared Area Monitors Entered" name="isAllSharedAreaMonitors" typeRef="boolean" />
<rule id="DecisionRule_07162mr">
<description>No shared spaces without Area Monitor</description>
<inputEntry id="UnaryTests_1p4ab2l">
<text>&gt;0</text>
</inputEntry>
<inputEntry id="UnaryTests_1jqxc3u">
<text>0</text>
</inputEntry>
<outputEntry id="LiteralExpression_16l50ps">
<text>true</text>
</outputEntry>
</rule>
<rule id="DecisionRule_0ifa4wu">
<description>One or more shared space without an Area Monitor</description>
<inputEntry id="UnaryTests_06bujee">
<text>&gt;0</text>
</inputEntry>
<inputEntry id="UnaryTests_0szbwxc">
<text>&gt; 0</text>
</inputEntry>
<outputEntry id="LiteralExpression_0td8sa6">
<text>false</text>
</outputEntry>
</rule>
<rule id="DecisionRule_1uh85sk">
<description>No shared spaces entered</description>
<inputEntry id="UnaryTests_15grk62">
<text>0</text>
</inputEntry>
<inputEntry id="UnaryTests_1gaiomm">
<text></text>
</inputEntry>
<outputEntry id="LiteralExpression_1iep8ai">
<text>true</text>
</outputEntry>
</rule>
</decisionTable>
</decision>
</definitions>

View File

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

View File

@ -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,34 @@ 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"])
# TODO: WRITE A TEST FOR STUDY FILES
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()
study = self.add_test_study()