def get_study(study_id, study_model: StudyModel = None): """Returns a study model that contains all the workflows organized by category. IMPORTANT: This is intended to be a lightweight call, it should never involve loading up and executing all the workflows in a study to calculate information.""" if not study_model: study_model = session.query(StudyModel).filter_by(id=study_id).first() study = Study.from_model(study_model) study.categories = StudyService.get_categories() workflow_metas = StudyService.__get_workflow_metas(study_id) study.approvals = ApprovalService.get_approvals_for_study(study.id) files = FileService.get_files_for_study(study.id) files = (File.from_models(model, FileService.get_file_data(model.id), 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. Don't execute this for Abandoned studies, as # we don't have the information to process them. if study.protocol_builder_status != ProtocolBuilderStatus.ABANDONED: status = StudyService.__get_study_status(study_model) study.warnings = StudyService.__update_status_of_workflow_meta(workflow_metas, status) # Group the workflows into their categories. for category in study.categories: category.workflows = {w for w in workflow_metas if w.category_id == category.id} return study
def get_image_file_data(self, fields_str, task): image_file_data = [] images_field_str = re.sub(r'[\[\]]', '', fields_str) images_field_keys = [ v.strip() for v in images_field_str.strip().split(',') ] for field_key in images_field_keys: if field_key in task.data: v = task.data[field_key] file_ids = v if isinstance(v, list) else [v] for file_id in file_ids: if isinstance(file_id, str) and file_id.isnumeric(): file_id = int(file_id) if file_id is not None and isinstance(file_id, int): if not task.workflow.data[ WorkflowProcessor.VALIDATION_PROCESS_KEY]: # Get the actual image data image_file_model = session.query( FileModel).filter_by(id=file_id).first() image_file_data_model = FileService.get_file_data( file_id, image_file_model) if image_file_data_model is not None: image_file_data.append(image_file_data_model) else: raise ApiError( code="not_a_file_id", message= "The CompleteTemplate script requires 2-3 arguments. The third argument should " "be a comma-delimited list of File IDs") return image_file_data
def get_file_data(file_id, version=None): file_data = FileService.get_file_data(file_id, version) if file_data is None: raise ApiError('no_such_file', 'The file id you provided does not exist') return send_file( io.BytesIO(file_data.data), attachment_filename=file_data.file_model.name, mimetype=file_data.file_model.content_type, cache_timeout=-1, # Don't cache these files on the browser. last_modified=file_data.date_created )
def to_file_api(file_model): """Converts a FileModel object to something we can return via the api""" if file_model.workflow_spec_id is not None: file_data_model = SpecFileService().get_spec_file_data(file_model.id) elif file_model.is_reference: file_data_model = ReferenceFileService().get_reference_file_data( file_model.name) else: file_data_model = FileService.get_file_data(file_model.id) return File.from_models(file_model, file_data_model, DocumentService.get_dictionary())
def get_document_directory(study_id, workflow_id=None): """ return a nested list of files arranged according to the category hierarchy defined in the doc dictionary """ file_models = FileService.get_files_for_study(study_id=study_id) doc_dict = DocumentService.get_dictionary() files = (File.from_models(model, FileService.get_file_data(model.id), doc_dict) for model in file_models) directory = DocumentService.get_directory(doc_dict, files, workflow_id) return DocumentDirectorySchema(many=True).dump(directory)
def get_study(study_id, study_model: StudyModel = None, do_status=False): """Returns a study model that contains all the workflows organized by category. IMPORTANT: This is intended to be a lightweight call, it should never involve loading up and executing all the workflows in a study to calculate information.""" if not study_model: study_model = session.query(StudyModel).filter_by( id=study_id).first() study = Study.from_model(study_model) study.create_user_display = LdapService.user_info( study.user_uid).display_name last_event: TaskEventModel = session.query(TaskEventModel) \ .filter_by(study_id=study_id, action='COMPLETE') \ .order_by(TaskEventModel.date.desc()).first() if last_event is None: study.last_activity_user = '******' study.last_activity_date = "" else: study.last_activity_user = LdapService.user_info( last_event.user_uid).display_name study.last_activity_date = last_event.date study.categories = StudyService.get_categories() workflow_metas = StudyService._get_workflow_metas(study_id) files = FileService.get_files_for_study(study.id) files = (File.from_models(model, FileService.get_file_data(model.id), DocumentService.get_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. Don't execute this for Abandoned studies, as # we don't have the information to process them. if study.status != StudyStatus.abandoned: # this line is taking 99% of the time that is used in get_study. # see ticket #196 if do_status: # __get_study_status() runs the master workflow to generate the status dictionary status = StudyService._get_study_status(study_model) study.warnings = StudyService._update_status_of_workflow_meta( workflow_metas, status) # Group the workflows into their categories. for category in study.categories: category.workflows = { w for w in workflow_metas if w.category_id == category.id } return study
def get_file_data(file_id, version=None): file_model = session.query(FileModel).filter( FileModel.id == file_id).first() if file_model is not None: file_data_model = FileService.get_file_data(file_id, version) if file_data_model is not None: return send_file( io.BytesIO(file_data_model.data), attachment_filename=file_model.name, mimetype=file_model.content_type, cache_timeout=-1 # Don't cache these files on the browser. ) else: raise ApiError( 'missing_data_model', f'The data model for file ({file_id}) does not exist') else: raise ApiError('missing_file_model', f'The file id you provided ({file_id}) does not exist')
def test_update_file_data(self): self.load_example_data() spec = session.query(WorkflowSpecModel).first() data = {} data['file'] = io.BytesIO( self.minimal_bpmn("abcdef")), 'my_new_file.bpmn' rv = self.app.post('/v1.0/file?workflow_spec_id=%s' % spec.id, data=data, follow_redirects=True, content_type='multipart/form-data', headers=self.logged_in_headers()) json_data = json.loads(rv.get_data(as_text=True)) file = FileModelSchema().load(json_data, session=session) data['file'] = io.BytesIO( self.minimal_bpmn("efghijk")), 'my_new_file.bpmn' rv = self.app.put('/v1.0/file/%i/data' % file.id, data=data, follow_redirects=True, content_type='multipart/form-data', headers=self.logged_in_headers()) self.assert_success(rv) self.assertIsNotNone(rv.get_data()) file_json = json.loads(rv.get_data(as_text=True)) self.assertEqual(2, file_json['latest_version']) self.assertEqual(FileType.bpmn.value, file_json['type']) self.assertEqual("application/octet-stream", file_json['content_type']) self.assertEqual(spec.id, file.workflow_spec_id) # Assure it is updated in the database and properly persisted. file_model = session.query(FileModel).filter( FileModel.id == file.id).first() file_data = FileService.get_file_data(file_model.id) self.assertEqual(2, file_data.version) rv = self.app.get('/v1.0/file/%i/data' % file.id, headers=self.logged_in_headers()) self.assert_success(rv) data = rv.get_data() self.assertIsNotNone(data) self.assertEqual(self.minimal_bpmn("efghijk"), data)
def get_file_data_link(file_id, auth_token, version=None): if not verify_token(auth_token): raise ApiError( 'not_authenticated', 'You need to include an authorization token in the URL with this') file_model = session.query(FileModel).filter( FileModel.id == file_id).first() if file_model.workflow_spec_id is not None: file_data = SpecFileService().get_spec_file_data(file_id) elif file_model.is_reference: file_data = ReferenceFileService().get_reference_file_data(file_id) else: file_data = FileService.get_file_data(file_id, version) if file_data is None: raise ApiError('no_such_file', f'The file id you provided ({file_id}) does not exist') return send_file( io.BytesIO(file_data.data), attachment_filename=file_model.name, mimetype=file_model.content_type, cache_timeout=-1, # Don't cache these files on the browser. last_modified=file_data.date_created, as_attachment=True)
def get_documents_status(study_id): """Returns a list of documents related to the study, and any file information that is available..""" # Get PB required docs, if Protocol Builder Service is enabled. if ProtocolBuilderService.is_enabled() and study_id is not None: try: pb_docs = ProtocolBuilderService.get_required_docs( study_id=study_id) except requests.exceptions.ConnectionError as ce: app.logger.error( f'Failed to connect to the Protocol Builder - {str(ce)}', exc_info=True) pb_docs = [] else: pb_docs = [] # Loop through all known document types, get the counts for those files, # and use pb_docs to mark those as required. doc_dictionary = DocumentService.get_dictionary() documents = {} for code, doc in doc_dictionary.items(): doc['required'] = False if ProtocolBuilderService.is_enabled() and doc['id'] != '': pb_data = next( (item for item in pb_docs['AUXDOCS'] if int(item['SS_AUXILIARY_DOC_TYPE_ID']) == int(doc['id']) ), None) if pb_data: doc['required'] = True doc['study_id'] = study_id doc['code'] = code # Make a display name out of categories name_list = [] for cat_key in ['category1', 'category2', 'category3']: if doc[cat_key] not in ['', 'NULL', None]: name_list.append(doc[cat_key]) doc['display_name'] = ' / '.join(name_list) # For each file, get associated workflow status doc_files = FileService.get_files_for_study(study_id=study_id, irb_doc_code=code) doc['count'] = len(doc_files) doc['files'] = [] for file_model in doc_files: file = File.from_models( file_model, FileService.get_file_data(file_model.id), []) file_data = FileSchema().dump(file) del file_data['document'] doc['files'].append(Box(file_data)) # update the document status to match the status of the workflow it is in. if 'status' not in doc or doc['status'] is None: status = session.query(WorkflowModel.status).filter_by( id=file.workflow_id).scalar() doc['status'] = status.value documents[code] = doc return Box(documents)
def to_file_api(file_model): """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())