A major refactor of how we search and store files, as there was a lot of confusing bits in here.
From an API point of view you can do the following (and only the following) /files?workflow_spec_id=x * You can find all files associated with a workflow_spec_id, and add a file with a workflow_spec_id /files?workflow_id=x * You can find all files associated with a workflow_id, and add a file that is directly associated with the workflow /files?workflow_id=x&form_field_key=y * You can find all files associated with a form element on a running workflow, and add a new file. Note: you can add multiple files to the same form_field_key, IF they have different file names. If the same name, the original file is archived, and the new file takes its place. The study endpoints always return a list of the file metadata associated with the study. Removed /studies-files, but there is an endpoint called /studies/all - that returns all the studies in the system, and does include their files. On a deeper level: The File model no longer contains: - study_id, - task_id, - form_field_key Instead, if the file is associated with workflow - then that is the one way it is connected to the study, and we use this relationship to find files for a study. A file is never associated with a task_id, as these change when the workflow is reloaded. The form_field_key must match the irb_doc_code, so when requesting files for a form field, we just look up the irb_doc_code.
This commit is contained in:
parent
560263d1a3
commit
cd7f67ab48
28
crc/api.yml
28
crc/api.yml
|
@ -82,7 +82,7 @@ paths:
|
|||
# /v1.0/study
|
||||
/study:
|
||||
get:
|
||||
operationId: crc.api.study.all_studies
|
||||
operationId: crc.api.study.user_studies
|
||||
summary: Provides a list of studies related to the current user.
|
||||
tags:
|
||||
- Studies
|
||||
|
@ -109,11 +109,13 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Study"
|
||||
/study-files:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Study"
|
||||
/study/all:
|
||||
get:
|
||||
operationId: crc.api.study.all_studies_and_files
|
||||
summary: Provides a list of studies with submitted files
|
||||
operationId: crc.api.study.all_studies
|
||||
summary: Provides a list of studies
|
||||
tags:
|
||||
- Studies
|
||||
responses:
|
||||
|
@ -122,7 +124,9 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Study"
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Study"
|
||||
/study/{study_id}:
|
||||
parameters:
|
||||
- name: study_id
|
||||
|
@ -353,24 +357,12 @@ paths:
|
|||
description: The unique id of a workflow specification
|
||||
schema:
|
||||
type: string
|
||||
- name: study_id
|
||||
in: query
|
||||
required: false
|
||||
description: The unique id of a study
|
||||
schema:
|
||||
type: integer
|
||||
- name: workflow_id
|
||||
in: query
|
||||
required: false
|
||||
description: The unique id of a workflow instance
|
||||
schema:
|
||||
type: integer
|
||||
- name: task_id
|
||||
in: query
|
||||
required: false
|
||||
description: The unique id of a workflow task
|
||||
schema:
|
||||
type: string
|
||||
- name: form_field_key
|
||||
in: query
|
||||
required: false
|
||||
|
|
|
@ -5,18 +5,20 @@ from flask import send_file
|
|||
|
||||
from crc import session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileModelSchema, FileModel, FileDataModel
|
||||
from crc.models.file import FileModelSchema, FileModel
|
||||
from crc.models.workflow import WorkflowSpecModel
|
||||
from crc.services.file_service import FileService
|
||||
|
||||
|
||||
def get_files(workflow_spec_id=None, study_id=None, workflow_id=None, task_id=None, form_field_key=None):
|
||||
if all(v is None for v in [workflow_spec_id, study_id, workflow_id, task_id, form_field_key]):
|
||||
def get_files(workflow_spec_id=None, workflow_id=None, form_field_key=None):
|
||||
if all(v is None for v in [workflow_spec_id, workflow_id, form_field_key]):
|
||||
raise ApiError('missing_parameter',
|
||||
'Please specify at least one of workflow_spec_id, study_id, '
|
||||
'workflow_id, and task_id for this file in the HTTP parameters')
|
||||
'Please specify either a workflow_spec_id or a '
|
||||
'workflow_id with an optional form_field_key')
|
||||
|
||||
results = FileService.get_files(workflow_spec_id, study_id, workflow_id, task_id, form_field_key)
|
||||
results = FileService.get_files(workflow_spec_id=workflow_spec_id,
|
||||
workflow_id=workflow_id,
|
||||
irb_doc_code=form_field_key)
|
||||
return FileModelSchema(many=True).dump(results)
|
||||
|
||||
|
||||
|
@ -25,25 +27,21 @@ def get_reference_files():
|
|||
return FileModelSchema(many=True).dump(results)
|
||||
|
||||
|
||||
def add_file(workflow_spec_id=None, study_id=None, workflow_id=None, task_id=None, form_field_key=None):
|
||||
all_none = all(v is None for v in [workflow_spec_id, study_id, workflow_id, task_id, form_field_key])
|
||||
missing_some = (workflow_spec_id is None) and (None in [study_id, workflow_id, form_field_key])
|
||||
if all_none or missing_some:
|
||||
raise ApiError('missing_parameter',
|
||||
'Please specify either a workflow_spec_id or all 3 of study_id, '
|
||||
'workflow_id, and field_id for this file in the HTTP parameters')
|
||||
if 'file' not in connexion.request.files:
|
||||
raise ApiError('invalid_file',
|
||||
'Expected a file named "file" in the multipart form request')
|
||||
|
||||
def add_file(workflow_spec_id=None, workflow_id=None, form_field_key=None):
|
||||
file = connexion.request.files['file']
|
||||
if workflow_spec_id:
|
||||
if workflow_id:
|
||||
if form_field_key is None:
|
||||
raise ApiError('invalid_workflow_file',
|
||||
'When adding a workflow related file, you must specify a form_field_key')
|
||||
file_model = FileService.add_workflow_file(workflow_id=workflow_id, irb_doc_code=form_field_key,
|
||||
name=file.filename, content_type=file.content_type,
|
||||
binary_data=file.stream.read())
|
||||
elif workflow_spec_id:
|
||||
workflow_spec = session.query(WorkflowSpecModel).filter_by(id=workflow_spec_id).first()
|
||||
file_model = FileService.add_workflow_spec_file(workflow_spec, file.filename, file.content_type,
|
||||
file.stream.read())
|
||||
else:
|
||||
file_model = FileService.add_form_field_file(study_id, workflow_id, task_id, form_field_key, file.filename,
|
||||
file.content_type, file.stream.read())
|
||||
raise ApiError("invalid_file", "You must supply either a workflow spec id or a workflow_id and form_field_key.")
|
||||
|
||||
return FileModelSchema().dump(file_model)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from sqlalchemy.exc import IntegrityError
|
|||
from crc import session
|
||||
from crc.api.common import ApiError, ApiErrorSchema
|
||||
from crc.models.protocol_builder import ProtocolBuilderStatus
|
||||
from crc.models.study import StudySchema, StudyFilesSchema, StudyModel, Study
|
||||
from crc.models.study import StudySchema, StudyModel, Study
|
||||
from crc.services.study_service import StudyService
|
||||
|
||||
|
||||
|
@ -65,7 +65,7 @@ def delete_study(study_id):
|
|||
raise ApiError(code="study_integrity_error", message=message)
|
||||
|
||||
|
||||
def all_studies():
|
||||
def user_studies():
|
||||
"""Returns all the studies associated with the current user. """
|
||||
StudyService.synch_with_protocol_builder_if_enabled(g.user)
|
||||
studies = StudyService.get_studies_for_user(g.user)
|
||||
|
@ -73,8 +73,8 @@ def all_studies():
|
|||
return results
|
||||
|
||||
|
||||
def all_studies_and_files():
|
||||
"""Returns all studies with submitted files"""
|
||||
studies = StudyService.get_studies_with_files()
|
||||
results = StudyFilesSchema(many=True).dump(studies)
|
||||
def all_studies():
|
||||
"""Returns all studies (regardless of user) with submitted files"""
|
||||
studies = StudyService.get_all_studies_with_files()
|
||||
results = StudySchema(many=True).dump(studies)
|
||||
return results
|
||||
|
|
|
@ -78,10 +78,7 @@ class FileModel(db.Model):
|
|||
primary_process_id = db.Column(db.String, nullable=True) # An id in the xml of BPMN documents, critical for primary BPMN.
|
||||
workflow_spec_id = db.Column(db.String, db.ForeignKey('workflow_spec.id'), nullable=True)
|
||||
workflow_id = db.Column(db.Integer, db.ForeignKey('workflow.id'), nullable=True)
|
||||
study_id = db.Column(db.Integer, db.ForeignKey('study.id'), nullable=True)
|
||||
task_id = db.Column(db.String, nullable=True)
|
||||
irb_doc_code = db.Column(db.String, nullable=True) # Code reference to the irb_documents.xlsx reference file.
|
||||
form_field_key = db.Column(db.String, nullable=True)
|
||||
latest_version = db.Column(db.Integer, default=0)
|
||||
|
||||
|
||||
|
|
|
@ -40,10 +40,6 @@ class StudyModel(db.Model):
|
|||
if self.on_hold:
|
||||
self.protocol_builder_status = ProtocolBuilderStatus.HOLD
|
||||
|
||||
def files(self):
|
||||
_files = FileModel.query.filter_by(workflow_id=self.workflow[0].id)
|
||||
return _files
|
||||
|
||||
|
||||
class WorkflowMetadata(object):
|
||||
def __init__(self, id, name, display_name, description, spec_version, category_id, state: WorkflowState, status: WorkflowStatus,
|
||||
|
@ -122,7 +118,7 @@ class Study(object):
|
|||
self.ind_number = ind_number
|
||||
self.categories = categories
|
||||
self.warnings = []
|
||||
|
||||
self.files = []
|
||||
|
||||
@classmethod
|
||||
def from_model(cls, study_model: StudyModel):
|
||||
|
@ -153,6 +149,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)
|
||||
|
||||
class Meta:
|
||||
model = Study
|
||||
|
@ -165,14 +162,3 @@ class StudySchema(ma.Schema):
|
|||
"""Can load the basic study data for updates to the database, but categories are write only"""
|
||||
return Study(**data)
|
||||
|
||||
|
||||
class StudyFilesSchema(ma.Schema):
|
||||
|
||||
files = fields.Method('_files')
|
||||
|
||||
class Meta:
|
||||
model = Study
|
||||
additional = ["id", "title", "last_updated", "primary_investigator_id"]
|
||||
|
||||
def _files(self, obj):
|
||||
return [file.name for file in obj.files()]
|
||||
|
|
|
@ -36,14 +36,11 @@ Takes two arguments:
|
|||
final_document_stream = self.process_template(task, study_id, workflow, *args, **kwargs)
|
||||
file_name = args[0]
|
||||
irb_doc_code = args[1]
|
||||
FileService.add_task_file(study_id=study_id,
|
||||
workflow_id=workflow_id,
|
||||
workflow_spec_id=workflow.workflow_spec_id,
|
||||
task_id=task.id,
|
||||
name=file_name,
|
||||
content_type=CONTENT_TYPES['docx'],
|
||||
binary_data=final_document_stream.read(),
|
||||
irb_doc_code=irb_doc_code)
|
||||
FileService.add_workflow_file(workflow_id=workflow_id,
|
||||
name=file_name,
|
||||
content_type=CONTENT_TYPES['docx'],
|
||||
binary_data=final_document_stream.read(),
|
||||
irb_doc_code=irb_doc_code)
|
||||
|
||||
def process_template(self, task, study_id, workflow=None, *args, **kwargs):
|
||||
"""Entry point, mostly worried about wiring it all up."""
|
||||
|
|
|
@ -10,7 +10,7 @@ from pandas import ExcelFile
|
|||
from crc import session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileType, FileDataModel, FileModel, LookupFileModel, LookupDataModel
|
||||
from crc.models.workflow import WorkflowSpecModel
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowModel
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
|
||||
|
||||
|
@ -40,31 +40,27 @@ class FileService(object):
|
|||
return code in df['code'].values
|
||||
|
||||
@staticmethod
|
||||
def add_form_field_file(study_id, workflow_id, task_id, form_field_key, name, content_type, binary_data):
|
||||
"""Create a new file and associate it with a user task form field within a workflow.
|
||||
Please note that the form_field_key MUST be a known file in the irb_documents.xslx reference document."""
|
||||
if not FileService.is_allowed_document(form_field_key):
|
||||
def add_workflow_file(workflow_id, irb_doc_code, name, content_type, binary_data):
|
||||
"""Create a new file and associate it with the workflow
|
||||
Please note that the irb_doc_code MUST be a known file in the irb_documents.xslx reference document."""
|
||||
if not FileService.is_allowed_document(irb_doc_code):
|
||||
raise ApiError("invalid_form_field_key",
|
||||
"When uploading files, the form field id must match a known document in the "
|
||||
"irb_docunents.xslx reference file. This code is not found in that file '%s'" % form_field_key)
|
||||
"irb_docunents.xslx reference file. This code is not found in that file '%s'" % irb_doc_code)
|
||||
|
||||
"""Assure this is unique to the workflow, task, and document code AND the Name
|
||||
Because we will allow users to upload multiple files for the same form field
|
||||
in some cases """
|
||||
file_model = session.query(FileModel)\
|
||||
.filter(FileModel.workflow_id == workflow_id)\
|
||||
.filter(FileModel.task_id == str(task_id))\
|
||||
.filter(FileModel.name == name)\
|
||||
.filter(FileModel.irb_doc_code == form_field_key).first()
|
||||
.filter(FileModel.irb_doc_code == irb_doc_code).first()
|
||||
|
||||
if not file_model:
|
||||
file_model = FileModel(
|
||||
study_id=study_id,
|
||||
workflow_id=workflow_id,
|
||||
task_id=task_id,
|
||||
name=name,
|
||||
form_field_key=form_field_key,
|
||||
irb_doc_code=form_field_key
|
||||
irb_doc_code=irb_doc_code
|
||||
)
|
||||
return FileService.update_file(file_model, binary_data, content_type)
|
||||
|
||||
|
@ -85,28 +81,6 @@ class FileService(object):
|
|||
df = df.set_index(index_column)
|
||||
return json.loads(df.to_json(orient='index'))
|
||||
|
||||
@staticmethod
|
||||
def add_task_file(study_id, workflow_id, workflow_spec_id, task_id, name, content_type, binary_data,
|
||||
irb_doc_code=None):
|
||||
|
||||
"""Assure this is unique to the workflow, task, and document code. Disregard name."""
|
||||
file_model = session.query(FileModel)\
|
||||
.filter(FileModel.workflow_id == workflow_id)\
|
||||
.filter(FileModel.task_id == str(task_id))\
|
||||
.filter(FileModel.irb_doc_code == irb_doc_code).first()
|
||||
|
||||
if not file_model:
|
||||
"""Create a new file and associate it with an executing task within a workflow."""
|
||||
file_model = FileModel(
|
||||
study_id=study_id,
|
||||
workflow_id=workflow_id,
|
||||
workflow_spec_id=workflow_spec_id,
|
||||
task_id=task_id,
|
||||
name=name,
|
||||
irb_doc_code=irb_doc_code
|
||||
)
|
||||
return FileService.update_file(file_model, binary_data, content_type)
|
||||
|
||||
@staticmethod
|
||||
def get_workflow_files(workflow_id):
|
||||
"""Returns all the file models associated with a running workflow."""
|
||||
|
@ -179,32 +153,29 @@ class FileService(object):
|
|||
return file_model
|
||||
|
||||
@staticmethod
|
||||
def get_files(workflow_spec_id=None,
|
||||
study_id=None, workflow_id=None, task_id=None, form_field_key=None,
|
||||
def get_files_for_study(study_id, irb_doc_code=None):
|
||||
query = session.query(FileModel).\
|
||||
join(WorkflowModel).\
|
||||
filter(WorkflowModel.study_id == study_id)
|
||||
if irb_doc_code:
|
||||
query = query.filter(FileModel.irb_doc_code == irb_doc_code)
|
||||
return query.all()
|
||||
|
||||
@staticmethod
|
||||
def get_files(workflow_spec_id=None, workflow_id=None,
|
||||
name=None, is_reference=False, irb_doc_code=None):
|
||||
query = session.query(FileModel).filter_by(is_reference=is_reference)
|
||||
if workflow_spec_id:
|
||||
query = query.filter_by(workflow_spec_id=workflow_spec_id)
|
||||
if all(v is None for v in [study_id, workflow_id, task_id, form_field_key]):
|
||||
query = query.filter_by(
|
||||
study_id=None,
|
||||
workflow_id=None,
|
||||
task_id=None,
|
||||
form_field_key=None,
|
||||
)
|
||||
else:
|
||||
if study_id:
|
||||
query = query.filter_by(study_id=study_id)
|
||||
if workflow_id:
|
||||
query = query.filter_by(workflow_id=workflow_id)
|
||||
if task_id:
|
||||
query = query.filter_by(task_id=str(task_id))
|
||||
if form_field_key:
|
||||
query = query.filter_by(form_field_key=form_field_key)
|
||||
if name:
|
||||
query = query.filter_by(name=name)
|
||||
elif workflow_id:
|
||||
query = query.filter_by(workflow_id=workflow_id)
|
||||
if irb_doc_code:
|
||||
query = query.filter_by(irb_doc_code=irb_doc_code)
|
||||
elif is_reference:
|
||||
query = query.filter_by(is_reference=True)
|
||||
|
||||
if name:
|
||||
query = query.filter_by(name=name)
|
||||
|
||||
results = query.all()
|
||||
return results
|
||||
|
|
|
@ -33,10 +33,15 @@ class StudyService(object):
|
|||
return studies
|
||||
|
||||
@staticmethod
|
||||
def get_studies_with_files():
|
||||
def get_all_studies_with_files():
|
||||
"""Returns a list of all studies"""
|
||||
db_studies = session.query(StudyModel).all()
|
||||
return db_studies
|
||||
studies = []
|
||||
for s in db_studies:
|
||||
study = Study.from_model(s)
|
||||
study.files = FileService.get_files_for_study(study.id)
|
||||
studies.append(study)
|
||||
return studies
|
||||
|
||||
@staticmethod
|
||||
def get_study(study_id, study_model: StudyModel = None):
|
||||
|
@ -48,6 +53,7 @@ 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)
|
||||
|
||||
# Calling this line repeatedly is very very slow. It creates the
|
||||
# master spec and runs it.
|
||||
|
@ -150,17 +156,15 @@ class StudyService(object):
|
|||
doc['display_name'] = ' / '.join(name_list)
|
||||
|
||||
# For each file, get associated workflow status
|
||||
doc_files = FileService.get_files(study_id=study_id, irb_doc_code=code)
|
||||
doc_files = FileService.get_files_for_study(study_id=study_id, irb_doc_code=code)
|
||||
doc['count'] = len(doc_files)
|
||||
doc['files'] = []
|
||||
for file in doc_files:
|
||||
doc['files'].append({'file_id': file.id,
|
||||
'task_id': file.task_id,
|
||||
'workflow_id': file.workflow_id,
|
||||
'workflow_spec_id': file.workflow_spec_id})
|
||||
'workflow_id': file.workflow_id})
|
||||
|
||||
# update the document status to match the status of the workflow it is in.
|
||||
if not 'status' in doc or doc['status'] is None:
|
||||
if 'status' not in doc or doc['status'] is None:
|
||||
workflow: WorkflowModel = session.query(WorkflowModel).filter_by(id=file.workflow_id).first()
|
||||
doc['status'] = workflow.status.value
|
||||
|
||||
|
|
|
@ -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" id="Definitions_96a17d9" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.4.1">
|
||||
<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" id="Definitions_96a17d9" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.3">
|
||||
<bpmn:process id="Process_93a29b3" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>SequenceFlow_0637d8i</bpmn:outgoing>
|
||||
|
@ -27,7 +27,7 @@
|
|||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>SequenceFlow_1i7hk1a</bpmn:incoming>
|
||||
<bpmn:outgoing>SequenceFlow_11c35oq</bpmn:outgoing>
|
||||
<bpmn:script>CompleteTemplate Letter.docx AncillaryDocument.CoCApplication</bpmn:script>
|
||||
<bpmn:script>CompleteTemplate Letter.docx AD_CoCApp</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:endEvent id="EndEvent_0evb22x">
|
||||
<bpmn:incoming>SequenceFlow_11c35oq</bpmn:incoming>
|
||||
|
@ -36,30 +36,30 @@
|
|||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_93a29b3">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0637d8i_di" bpmnElement="SequenceFlow_0637d8i">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="265" y="117" />
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_11c35oq_di" bpmnElement="SequenceFlow_11c35oq">
|
||||
<di:waypoint x="565" y="117" />
|
||||
<di:waypoint x="665" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="UserTask_02o51o8_di" bpmnElement="task_gather_information">
|
||||
<dc:Bounds x="265" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_1i7hk1a_di" bpmnElement="SequenceFlow_1i7hk1a">
|
||||
<di:waypoint x="365" y="117" />
|
||||
<di:waypoint x="465" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0637d8i_di" bpmnElement="SequenceFlow_0637d8i">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="265" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="UserTask_02o51o8_di" bpmnElement="task_gather_information">
|
||||
<dc:Bounds x="265" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="ScriptTask_0xjh8x4_di" bpmnElement="task_generate_document">
|
||||
<dc:Bounds x="465" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="EndEvent_0evb22x_di" bpmnElement="EndEvent_0evb22x">
|
||||
<dc:Bounds x="665" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_11c35oq_di" bpmnElement="SequenceFlow_11c35oq">
|
||||
<di:waypoint x="565" y="117" />
|
||||
<di:waypoint x="665" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
||||
|
|
|
@ -37,11 +37,9 @@ class TestApprovalsService(BaseTest):
|
|||
ApprovalService.add_approval(study_id=workflow.study_id, workflow_id=workflow.id, approver_uid="dhf8r")
|
||||
|
||||
irb_code_1 = "UVACompl_PRCAppr" # The first file referenced in pb required docs.
|
||||
FileService.add_task_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
workflow_spec_id=workflow.workflow_spec_id,
|
||||
task_id=task.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code_1)
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code_1)
|
||||
|
||||
ApprovalService.add_approval(study_id=workflow.study_id, workflow_id=workflow.id, approver_uid="dhf8r")
|
||||
self.assertEquals(2, db.session.query(ApprovalModel).count())
|
||||
|
@ -59,22 +57,16 @@ class TestApprovalsService(BaseTest):
|
|||
irb_code_1 = "UVACompl_PRCAppr" # The first file referenced in pb required docs.
|
||||
irb_code_2 = "NonUVAIRB_AssuranceForm" # The second file in above.
|
||||
# Add a task file to the workflow.
|
||||
FileService.add_task_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
workflow_spec_id=workflow.workflow_spec_id,
|
||||
task_id=task.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code_1)
|
||||
# Add a two form field files with the same irb_code, but
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
task_id=task.id,
|
||||
form_field_key=irb_code_2,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234')
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
form_field_key=irb_code_2,
|
||||
task_id=task.id,
|
||||
name="another_anything.png", content_type="text",
|
||||
binary_data=b'5678')
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code_1)
|
||||
# Add a two form field files with the same irb_code, but different names
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code=irb_code_2)
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="another_anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code_2)
|
||||
|
||||
|
||||
# Workflow hash should look be id[1]-id[1]-id[1]
|
||||
|
@ -85,10 +77,9 @@ class TestApprovalsService(BaseTest):
|
|||
|
||||
# Replace last file
|
||||
# should now be id[1]-id[1]-id[2]
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
form_field_key=irb_code_2,
|
||||
task_id=task.id,
|
||||
name="another_anything.png", content_type="text",
|
||||
binary_data=b'9999')
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
irb_doc_code=irb_code_2,
|
||||
name="another_anything.png", content_type="text",
|
||||
binary_data=b'9999')
|
||||
self.assertRegexpMatches(ApprovalService._generate_workflow_hash(latest_files), "\d+\[1\]-\d+\[1\]-\d+\[2\]")
|
||||
|
||||
|
|
|
@ -13,17 +13,13 @@ class TestFileService(BaseTest):
|
|||
processor = WorkflowProcessor(workflow)
|
||||
task = processor.next_task()
|
||||
irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs.
|
||||
FileService.add_task_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
workflow_spec_id=workflow.workflow_spec_id,
|
||||
task_id=task.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code=irb_code)
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code=irb_code)
|
||||
# Add the file again with different data
|
||||
FileService.add_task_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
workflow_spec_id=workflow.workflow_spec_id,
|
||||
task_id=task.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code)
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code=irb_code)
|
||||
|
||||
file_models = FileService.get_workflow_files(workflow_id=workflow.id)
|
||||
self.assertEquals(1, len(file_models))
|
||||
|
@ -36,17 +32,15 @@ class TestFileService(BaseTest):
|
|||
processor = WorkflowProcessor(workflow)
|
||||
task = processor.next_task()
|
||||
irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs.
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
task_id=task.id,
|
||||
form_field_key=irb_code,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234')
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
irb_doc_code=irb_code,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234')
|
||||
# Add the file again with different data
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
form_field_key=irb_code,
|
||||
task_id=task.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678')
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
irb_doc_code=irb_code,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678')
|
||||
|
||||
file_models = FileService.get_workflow_files(workflow_id=workflow.id)
|
||||
self.assertEquals(1, len(file_models))
|
||||
|
@ -59,17 +53,15 @@ class TestFileService(BaseTest):
|
|||
processor = WorkflowProcessor(workflow)
|
||||
task = processor.next_task()
|
||||
irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs.
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
task_id=task.id,
|
||||
form_field_key=irb_code,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234')
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
irb_doc_code=irb_code,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234')
|
||||
# Add the file again with different data
|
||||
FileService.add_form_field_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
form_field_key=irb_code,
|
||||
task_id=task.id,
|
||||
name="a_different_thing.png", content_type="text",
|
||||
binary_data=b'5678')
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
irb_doc_code=irb_code,
|
||||
name="a_different_thing.png", content_type="text",
|
||||
binary_data=b'5678')
|
||||
file_models = FileService.get_workflow_files(workflow_id=workflow.id)
|
||||
self.assertEquals(2, len(file_models))
|
||||
self.assertEquals(1, file_models[0].latest_version)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import io
|
||||
import json
|
||||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
from crc import session
|
||||
from crc.models.file import FileModel, FileType, FileModelSchema, FileDataModel
|
||||
|
@ -9,7 +9,6 @@ from crc.models.workflow import WorkflowSpecModel
|
|||
from crc.services.file_service import FileService
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from example_data import ExampleDataLoader
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestFilesApi(BaseTest):
|
||||
|
|
|
@ -2,6 +2,8 @@ import json
|
|||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
from crc import db, app
|
||||
from crc.models.protocol_builder import ProtocolBuilderStatus
|
||||
from crc.models.study import StudyModel
|
||||
|
@ -12,7 +14,6 @@ from crc.services.file_service import FileService
|
|||
from crc.services.study_service import StudyService
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from example_data import ExampleDataLoader
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
class TestStudyService(BaseTest):
|
||||
|
@ -143,11 +144,9 @@ class TestStudyService(BaseTest):
|
|||
# Add a document to the study with the correct code.
|
||||
workflow = self.create_workflow('docx')
|
||||
irb_code = "UVACompl_PRCAppr" # The first file referenced in pb required docs.
|
||||
FileService.add_task_file(study_id=workflow.study_id, workflow_id=workflow.id,
|
||||
workflow_spec_id=workflow.workflow_spec_id,
|
||||
task_id="fakingthisout",
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code=irb_code)
|
||||
FileService.add_workflow_file(workflow_id=workflow.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code=irb_code)
|
||||
|
||||
docs = StudyService().get_documents_status(workflow.study_id)
|
||||
self.assertIsNotNone(docs)
|
||||
|
@ -156,13 +155,31 @@ class TestStudyService(BaseTest):
|
|||
self.assertIsNotNone(docs["UVACompl_PRCAppr"]['files'][0])
|
||||
self.assertIsNotNone(docs["UVACompl_PRCAppr"]['files'][0]['file_id'])
|
||||
self.assertEquals(workflow.id, docs["UVACompl_PRCAppr"]['files'][0]['workflow_id'])
|
||||
self.assertEquals(workflow.workflow_spec_id, docs["UVACompl_PRCAppr"]['files'][0]['workflow_spec_id'])
|
||||
|
||||
# 'file_id': 123,
|
||||
# 'task_id': 'abcdef14236890',
|
||||
# 'workflow_id': 456,
|
||||
# 'workflow_spec_id': 'irb_api_details',
|
||||
# 'status': 'complete',
|
||||
def test_get_all_studies(self):
|
||||
user = self.create_user_with_study_and_workflow()
|
||||
|
||||
# Add a document to the study with the correct code.
|
||||
workflow1 = self.create_workflow('docx')
|
||||
workflow2 = self.create_workflow('empty_workflow')
|
||||
|
||||
# Add files to both workflows.
|
||||
FileService.add_workflow_file(workflow_id=workflow1.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code="UVACompl_PRCAppr" )
|
||||
FileService.add_workflow_file(workflow_id=workflow1.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code="AD_Consent_Model")
|
||||
FileService.add_workflow_file(workflow_id=workflow2.id,
|
||||
name="anything.png", content_type="text",
|
||||
binary_data=b'1234', irb_doc_code="UVACompl_PRCAppr" )
|
||||
|
||||
studies = StudyService().get_all_studies_with_files()
|
||||
self.assertEquals(1, len(studies))
|
||||
self.assertEquals(3, len(studies[0].files))
|
||||
|
||||
|
||||
|
||||
|
||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_investigators') # mock_docs
|
||||
def test_get_personnel(self, mock_docs):
|
||||
|
|
|
@ -223,10 +223,10 @@ class TestWorkflowProcessor(BaseTest):
|
|||
self._populate_form_with_random_data(task)
|
||||
processor.complete_task(task)
|
||||
|
||||
files = session.query(FileModel).filter_by(study_id=study.id, workflow_id=processor.get_workflow_id()).all()
|
||||
files = session.query(FileModel).filter_by(workflow_id=processor.get_workflow_id()).all()
|
||||
self.assertEqual(0, len(files))
|
||||
processor.do_engine_steps()
|
||||
files = session.query(FileModel).filter_by(study_id=study.id, workflow_id=processor.get_workflow_id()).all()
|
||||
files = session.query(FileModel).filter_by(workflow_id=processor.get_workflow_id()).all()
|
||||
self.assertEqual(1, len(files), "The task should create a new file.")
|
||||
file_data = session.query(FileDataModel).filter(FileDataModel.file_model_id == files[0].id).first()
|
||||
self.assertIsNotNone(file_data.data)
|
||||
|
|
Loading…
Reference in New Issue