From cd7f67ab48b7bec858f5d5eb6a34be556f4d029c Mon Sep 17 00:00:00 2001 From: Dan Funk Date: Thu, 28 May 2020 08:27:26 -0400 Subject: [PATCH] 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. --- crc/api.yml | 28 ++++------- crc/api/file.py | 38 ++++++++------- crc/api/study.py | 12 ++--- crc/models/file.py | 3 -- crc/models/study.py | 18 +------- crc/scripts/complete_template.py | 13 ++---- crc/services/file_service.py | 79 ++++++++++---------------------- crc/services/study_service.py | 18 +++++--- tests/data/docx/docx.bpmn | 30 ++++++------ tests/test_approvals_service.py | 43 +++++++---------- tests/test_file_service.py | 52 +++++++++------------ tests/test_files_api.py | 5 +- tests/test_study_service.py | 41 ++++++++++++----- tests/test_workflow_processor.py | 4 +- 14 files changed, 164 insertions(+), 220 deletions(-) diff --git a/crc/api.yml b/crc/api.yml index 2160eef2..c061307d 100644 --- a/crc/api.yml +++ b/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 diff --git a/crc/api/file.py b/crc/api/file.py index 706df77e..733dcd34 100644 --- a/crc/api/file.py +++ b/crc/api/file.py @@ -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) diff --git a/crc/api/study.py b/crc/api/study.py index 34ca4a3e..423f6fe2 100644 --- a/crc/api/study.py +++ b/crc/api/study.py @@ -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 diff --git a/crc/models/file.py b/crc/models/file.py index a96583b8..c351d2a7 100644 --- a/crc/models/file.py +++ b/crc/models/file.py @@ -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) diff --git a/crc/models/study.py b/crc/models/study.py index 2af9aeb7..283196e2 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -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()] diff --git a/crc/scripts/complete_template.py b/crc/scripts/complete_template.py index 46daba6b..2acc1c4a 100644 --- a/crc/scripts/complete_template.py +++ b/crc/scripts/complete_template.py @@ -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.""" diff --git a/crc/services/file_service.py b/crc/services/file_service.py index 4e249675..493f33ec 100644 --- a/crc/services/file_service.py +++ b/crc/services/file_service.py @@ -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 diff --git a/crc/services/study_service.py b/crc/services/study_service.py index db5c2b72..68f760e1 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -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 diff --git a/tests/data/docx/docx.bpmn b/tests/data/docx/docx.bpmn index 1c9d766a..a95feb07 100644 --- a/tests/data/docx/docx.bpmn +++ b/tests/data/docx/docx.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_0637d8i @@ -27,7 +27,7 @@ SequenceFlow_1i7hk1a SequenceFlow_11c35oq - CompleteTemplate Letter.docx AncillaryDocument.CoCApplication + CompleteTemplate Letter.docx AD_CoCApp SequenceFlow_11c35oq @@ -36,30 +36,30 @@ - - - - - - + + + - - - + + + + + + + + + + - - - - diff --git a/tests/test_approvals_service.py b/tests/test_approvals_service.py index dcc557e9..0ae37941 100644 --- a/tests/test_approvals_service.py +++ b/tests/test_approvals_service.py @@ -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\]") diff --git a/tests/test_file_service.py b/tests/test_file_service.py index 29026117..0b5ae5cf 100644 --- a/tests/test_file_service.py +++ b/tests/test_file_service.py @@ -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) diff --git a/tests/test_files_api.py b/tests/test_files_api.py index d7e31f80..79f8dbf2 100644 --- a/tests/test_files_api.py +++ b/tests/test_files_api.py @@ -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): diff --git a/tests/test_study_service.py b/tests/test_study_service.py index d7e522da..aa77cc24 100644 --- a/tests/test_study_service.py +++ b/tests/test_study_service.py @@ -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): diff --git a/tests/test_workflow_processor.py b/tests/test_workflow_processor.py index e19bbc87..fe182e28 100644 --- a/tests/test_workflow_processor.py +++ b/tests/test_workflow_processor.py @@ -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)