import enum import marshmallow from marshmallow import INCLUDE, fields from sqlalchemy import func from crc import db, ma, app from crc.api.common import ApiError from crc.models.file import FileDataModel from crc.models.ldap import LdapSchema from crc.models.study import StudyModel from crc.models.workflow import WorkflowModel from crc.services.file_service import FileService from crc.services.ldap_service import LdapService class ApprovalStatus(enum.Enum): 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. # Used for overall status only, never set on a task. AWAITING = "AWAITING" # awaiting another approval class ApprovalFile(db.Model): file_data_id = db.Column(db.Integer, db.ForeignKey(FileDataModel.id), primary_key=True) approval_id = db.Column(db.Integer, db.ForeignKey("approval.id"), primary_key=True) approval = db.relationship("ApprovalModel") file_data = db.relationship(FileDataModel) class ApprovalModel(db.Model): __tablename__ = 'approval' id = db.Column(db.Integer, primary_key=True) study_id = db.Column(db.Integer, db.ForeignKey(StudyModel.id), nullable=False) study = db.relationship(StudyModel) workflow_id = db.Column(db.Integer, db.ForeignKey(WorkflowModel.id), nullable=False) workflow = db.relationship(WorkflowModel) approver_uid = db.Column(db.String) # Not linked to user model, as they may not have logged in yet. status = db.Column(db.String) message = db.Column(db.String, default='') date_created = db.Column(db.DateTime(timezone=True), default=func.now()) date_approved = db.Column(db.DateTime(timezone=True), default=None) version = db.Column(db.Integer) # Incremented integer, so 1,2,3 as requests are made. approval_files = db.relationship(ApprovalFile, back_populates="approval", cascade="all, delete, delete-orphan", order_by=ApprovalFile.file_data_id) class Approval(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) @classmethod def from_model(cls, model: ApprovalModel): args = dict((k, v) for k, v in model.__dict__.items() if not k.startswith('_')) instance = cls(**args) instance.related_approvals = [] instance.title = model.study.title if model.study else '' try: instance.approver = LdapService.user_info(model.approver_uid) instance.primary_investigator = LdapService.user_info(model.study.primary_investigator_id) except ApiError as ae: app.logger.error(f'Ldap lookup failed for approval record {model.id}', exc_info=True) doc_dictionary = FileService.get_doc_dictionary() instance.associated_files = [] for approval_file in model.approval_files: try: # fixme: This is slow because we are doing a ton of queries to find the irb code. 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 if extra_info: associated_file['name'] = '_'.join((extra_info['category1'], 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'] = '(' + model.study.primary_investigator_id + ')' + associated_file['name'] associated_file['content_type'] = approval_file.file_data.file_model.content_type instance.associated_files.append(associated_file) return instance def update_model(self, approval_model: ApprovalModel): approval_model.status = self.status approval_model.message = self.message class ApprovalSchema(ma.Schema): approver = fields.Nested(LdapSchema, dump_only=True) primary_investigator = fields.Nested(LdapSchema, dump_only=True) related_approvals = fields.List(fields.Nested('ApprovalSchema', allow_none=True, dump_only=True)) class Meta: model = Approval fields = ["id", "study_id", "workflow_id", "version", "title", "status", "message", "approver", "primary_investigator", "associated_files", "date_created", "date_approved", "related_approvals"] unknown = INCLUDE @marshmallow.post_load def make_approval(self, data, **kwargs): """Loads the basic approval data for updates to the database""" return Approval(**data)