mirror of
https://github.com/sartography/cr-connect-workflow.git
synced 2025-02-23 13:18:35 +00:00
181 lines
7.6 KiB
Python
181 lines
7.6 KiB
Python
import json
|
|
from datetime import datetime
|
|
|
|
from SpiffWorkflow.util.metrics import timeit, firsttime, sincetime
|
|
from flask import g, send_file
|
|
from sqlalchemy.exc import IntegrityError
|
|
from crc import session
|
|
from crc.api.common import ApiError, ApiErrorSchema
|
|
from crc.models.study import Study, StudyEventType, StudyModel, StudySchema, StudyForUpdateSchema, \
|
|
StudyStatus, StudyAssociatedSchema, Category
|
|
from crc.models.task_log import TaskLogQuery, TaskLogQuerySchema
|
|
from crc.services.spreadsheet_service import SpreadsheetService
|
|
from crc.services.study_service import StudyService
|
|
from crc.services.task_logging_service import TaskLoggingService
|
|
from crc.services.user_service import UserService
|
|
from crc.services.workflow_processor import WorkflowProcessor
|
|
from crc.services.workflow_service import WorkflowService
|
|
from crc.services.workflow_spec_service import WorkflowSpecService
|
|
from crc.api.user import verify_token
|
|
|
|
import io
|
|
|
|
|
|
def add_study(body):
|
|
# fixme: Remove this method. We don't add a study this way except in testing.
|
|
"""
|
|
This method seems to be used by one test, and no where else!
|
|
Or any study like object. Body should include a title, and primary_investigator_id """
|
|
"""Or any study like object. Body should include a title """
|
|
if 'title' not in body:
|
|
raise ApiError("missing_title", "Can't create a new study without a title.")
|
|
|
|
study_model = StudyModel(user_uid=UserService.current_user().uid,
|
|
title=body['title'],
|
|
last_updated=datetime.utcnow(),
|
|
status=StudyStatus.in_progress,
|
|
review_type=body['review_type'])
|
|
session.add(study_model)
|
|
StudyService.add_study_update_event(study_model,
|
|
status=StudyStatus.in_progress,
|
|
event_type=StudyEventType.user,
|
|
user_uid=g.user.uid)
|
|
|
|
spec_service = WorkflowSpecService()
|
|
specs = spec_service.get_specs()
|
|
categories = spec_service.get_categories()
|
|
errors = StudyService.add_all_workflow_specs_to_study(study_model, specs)
|
|
session.commit()
|
|
|
|
master_workflow_results = __run_master_spec(study_model, spec_service.master_spec)
|
|
study = StudyService().get_study(study_model.id, categories, master_workflow_results=master_workflow_results,
|
|
process_categories=True)
|
|
study_data = StudySchema().dump(study)
|
|
study_data["errors"] = ApiErrorSchema(many=True).dump(errors)
|
|
return study_data
|
|
|
|
|
|
def __run_master_spec(study_model, master_spec):
|
|
"""Runs the master workflow spec to get details on the status of each workflow.
|
|
This is a fairly expensive call."""
|
|
"""Uses the Top Level Workflow to calculate the status of the study, and its
|
|
workflow models."""
|
|
if not master_spec:
|
|
raise ApiError("missing_master_spec", "No specifications are currently marked as the master spec.")
|
|
return WorkflowProcessor.run_master_spec(master_spec, study_model)
|
|
|
|
|
|
def update_study(study_id, body):
|
|
spec_service = WorkflowSpecService()
|
|
categories = spec_service.get_categories()
|
|
|
|
"""Pretty limited, but allows manual modifications to the study status """
|
|
if study_id is None:
|
|
raise ApiError('unknown_study', 'Please provide a valid Study ID.')
|
|
|
|
study_model = session.query(StudyModel).filter_by(id=study_id).first()
|
|
if study_model is None:
|
|
raise ApiError('unknown_study', 'The study "' + study_id + '" is not recognized.')
|
|
|
|
study: Study = StudyForUpdateSchema().load(body)
|
|
|
|
status = StudyStatus(study.status)
|
|
study_model.last_updated = datetime.utcnow()
|
|
|
|
if study_model.status != status:
|
|
study_model.status = status
|
|
StudyService.add_study_update_event(study_model, status, StudyEventType.user,
|
|
user_uid=UserService.current_user().uid if UserService.has_user() else None,
|
|
comment='' if not hasattr(study, 'comment') else study.comment,
|
|
)
|
|
|
|
if status == StudyStatus.open_for_enrollment:
|
|
study_model.enrollment_date = study.enrollment_date
|
|
|
|
session.add(study_model)
|
|
session.commit()
|
|
|
|
if status == StudyStatus.abandoned or status == StudyStatus.hold:
|
|
WorkflowService.process_workflows_for_cancels(study_id)
|
|
|
|
# Need to reload the full study to return it to the frontend
|
|
study = StudyService.get_study(study_id, categories)
|
|
return StudySchema().dump(study)
|
|
|
|
|
|
def get_study(study_id, update_status=False):
|
|
spec_service = WorkflowSpecService()
|
|
categories = spec_service.get_categories()
|
|
master_workflow_results = []
|
|
if update_status:
|
|
study_model = session.query(StudyModel).filter(StudyModel.id == study_id).first()
|
|
master_workflow_results = __run_master_spec(study_model, spec_service.master_spec)
|
|
study = StudyService().get_study(study_id, categories, master_workflow_results=master_workflow_results, process_categories=True)
|
|
if (study is None):
|
|
raise ApiError("unknown_study", 'The study "' + study_id + '" is not recognized.', status_code=404)
|
|
return StudySchema().dump(study)
|
|
|
|
|
|
def get_study_associates(study_id):
|
|
return StudyAssociatedSchema(many=True).dump(StudyService.get_study_associates(study_id))
|
|
|
|
|
|
def get_logs_for_study(study_id, body):
|
|
task_log_query = TaskLogQuery(**body)
|
|
task_log_query.study_id = study_id # Force the study id
|
|
return TaskLogQuerySchema().dump(
|
|
TaskLoggingService.get_logs_for_study_paginated(study_id, task_log_query))
|
|
|
|
|
|
def download_logs_for_study(study_id, auth_token):
|
|
# Download links incorporate an auth token in the request for direct download
|
|
if not verify_token(auth_token):
|
|
raise ApiError('not_authenticated', 'You need to include an authorization token in the URL with this')
|
|
|
|
title = f'Study {study_id}'
|
|
logs, headers = TaskLoggingService.get_log_data_for_download(study_id)
|
|
spreadsheet = SpreadsheetService.create_spreadsheet(logs, headers, title)
|
|
|
|
return send_file(
|
|
io.BytesIO(spreadsheet),
|
|
attachment_filename='logs.xlsx',
|
|
mimetype='xlsx',
|
|
cache_timeout=-1, # Don't cache these files on the browser.
|
|
last_modified=datetime.now(),
|
|
as_attachment=True
|
|
)
|
|
|
|
|
|
def delete_study(study_id):
|
|
try:
|
|
StudyService.delete_study(study_id)
|
|
except IntegrityError as ie:
|
|
session.rollback()
|
|
message = "Failed to delete Study #%i due to an Integrity Error: %s" % (study_id, str(ie))
|
|
raise ApiError(code="study_integrity_error", message=message)
|
|
|
|
|
|
def user_studies():
|
|
"""Returns all the studies associated with the current user. """
|
|
user = UserService.current_user(allow_admin_impersonate=True)
|
|
spec_service = WorkflowSpecService()
|
|
specs = spec_service.get_specs()
|
|
cats = spec_service.get_categories()
|
|
StudyService.synch_with_protocol_builder_if_enabled(user, specs)
|
|
studies = StudyService().get_studies_for_user(user, categories=cats)
|
|
if len(studies) == 0:
|
|
studies = StudyService().get_studies_for_user(user, categories=cats, include_invalid=True)
|
|
if len(studies) > 0:
|
|
message = f"All studies associated with User: {user.uid} failed study validation"
|
|
raise ApiError(code="study_integrity_error", message=message)
|
|
|
|
results = StudySchema(many=True).dump(studies)
|
|
return results
|
|
|
|
|
|
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
|