2020-05-22 22:25:00 +00:00
|
|
|
import enum
|
|
|
|
|
2020-05-29 08:51:50 +00:00
|
|
|
import marshmallow
|
2020-06-02 22:17:00 +00:00
|
|
|
from marshmallow import INCLUDE, fields
|
2020-05-23 19:08:17 +00:00
|
|
|
from sqlalchemy import func
|
2020-05-22 22:25:00 +00:00
|
|
|
|
2020-06-02 22:17:00 +00:00
|
|
|
from crc import db, ma, app
|
2020-05-25 23:30:16 +00:00
|
|
|
from crc.api.common import ApiError
|
2020-05-29 08:51:50 +00:00
|
|
|
from crc.models.file import FileDataModel
|
2020-06-02 22:17:00 +00:00
|
|
|
from crc.models.ldap import LdapSchema
|
2020-05-22 22:25:00 +00:00
|
|
|
from crc.models.study import StudyModel
|
|
|
|
from crc.models.workflow import WorkflowModel
|
2020-05-31 23:24:23 +00:00
|
|
|
from crc.services.file_service import FileService
|
2020-06-02 22:17:00 +00:00
|
|
|
from crc.services.ldap_service import LdapService
|
2020-05-22 22:25:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ApprovalStatus(enum.Enum):
|
2020-05-31 19:35:42 +00:00
|
|
|
PENDING = "PENDING" # no one has done jack.
|
2020-05-22 22:25:00 +00:00
|
|
|
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.
|
|
|
|
|
2020-06-05 18:33:00 +00:00
|
|
|
# Used for overall status only, never set on a task.
|
|
|
|
AWAITING = "AWAITING" # awaiting another approval
|
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
|
2020-05-23 19:08:17 +00:00
|
|
|
class ApprovalFile(db.Model):
|
2020-05-29 00:03:50 +00:00
|
|
|
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)
|
2020-05-23 19:08:17 +00:00
|
|
|
|
|
|
|
approval = db.relationship("ApprovalModel")
|
2020-05-29 00:03:50 +00:00
|
|
|
file_data = db.relationship(FileDataModel)
|
2020-05-23 19:08:17 +00:00
|
|
|
|
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
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)
|
2020-06-02 22:17:00 +00:00
|
|
|
study = db.relationship(StudyModel)
|
2020-05-22 22:25:00 +00:00
|
|
|
workflow_id = db.Column(db.Integer, db.ForeignKey(WorkflowModel.id), nullable=False)
|
2020-05-24 05:53:48 +00:00
|
|
|
workflow = db.relationship(WorkflowModel)
|
2020-05-22 22:25:00 +00:00
|
|
|
approver_uid = db.Column(db.String) # Not linked to user model, as they may not have logged in yet.
|
|
|
|
status = db.Column(db.String)
|
2020-05-27 18:06:32 +00:00
|
|
|
message = db.Column(db.String, default='')
|
2020-05-23 19:08:17 +00:00
|
|
|
date_created = db.Column(db.DateTime(timezone=True), default=func.now())
|
2020-06-02 22:17:00 +00:00
|
|
|
date_approved = db.Column(db.DateTime(timezone=True), default=None)
|
2020-05-23 19:08:17 +00:00
|
|
|
version = db.Column(db.Integer) # Incremented integer, so 1,2,3 as requests are made.
|
2020-05-29 00:03:50 +00:00
|
|
|
approval_files = db.relationship(ApprovalFile, back_populates="approval",
|
|
|
|
cascade="all, delete, delete-orphan",
|
|
|
|
order_by=ApprovalFile.file_data_id)
|
2020-05-23 19:08:17 +00:00
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
|
|
|
|
class Approval(object):
|
|
|
|
|
2020-05-25 21:40:24 +00:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
self.__dict__.update(kwargs)
|
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
@classmethod
|
2020-06-04 18:59:36 +00:00
|
|
|
def from_model(cls, model: ApprovalModel):
|
2020-05-25 21:40:24 +00:00
|
|
|
# TODO: Reduce the code by iterating over model's dict keys
|
2020-05-22 22:25:00 +00:00
|
|
|
instance = cls()
|
|
|
|
instance.id = model.id
|
2020-05-24 05:53:48 +00:00
|
|
|
instance.study_id = model.study_id
|
|
|
|
instance.workflow_id = model.workflow_id
|
2020-05-23 19:08:17 +00:00
|
|
|
instance.version = model.version
|
2020-05-22 22:25:00 +00:00
|
|
|
instance.approver_uid = model.approver_uid
|
|
|
|
instance.status = model.status
|
2020-05-24 05:53:48 +00:00
|
|
|
instance.message = model.message
|
|
|
|
instance.date_created = model.date_created
|
2020-06-02 23:36:06 +00:00
|
|
|
instance.date_approved = model.date_approved
|
2020-05-24 05:53:48 +00:00
|
|
|
instance.version = model.version
|
|
|
|
instance.title = ''
|
2020-06-02 22:17:00 +00:00
|
|
|
instance.related_approvals = []
|
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
if model.study:
|
|
|
|
instance.title = model.study.title
|
2020-05-25 23:30:16 +00:00
|
|
|
try:
|
2020-06-04 18:59:36 +00:00
|
|
|
instance.approver = LdapService.user_info(model.approver_uid)
|
|
|
|
instance.primary_investigator = LdapService.user_info(model.study.primary_investigator_id)
|
2020-06-02 22:17:00 +00:00
|
|
|
except ApiError as ae:
|
|
|
|
app.logger.error("Ldap lookup failed for approval record %i" % model.id)
|
2020-05-24 05:53:48 +00:00
|
|
|
|
2020-06-04 18:59:36 +00:00
|
|
|
doc_dictionary = FileService.get_doc_dictionary()
|
2020-05-24 05:53:48 +00:00
|
|
|
instance.associated_files = []
|
|
|
|
for approval_file in model.approval_files:
|
2020-05-31 23:24:23 +00:00
|
|
|
try:
|
2020-06-05 21:49:55 +00:00
|
|
|
# fixme: This is slow because we are doing a ton of queries to find the irb code.
|
2020-05-31 23:24:23 +00:00
|
|
|
extra_info = doc_dictionary[approval_file.file_data.file_model.irb_doc_code]
|
|
|
|
except:
|
|
|
|
extra_info = None
|
2020-05-24 05:53:48 +00:00
|
|
|
associated_file = {}
|
2020-05-29 09:04:18 +00:00
|
|
|
associated_file['id'] = approval_file.file_data.file_model.id
|
2020-05-31 23:24:23 +00:00
|
|
|
if extra_info:
|
2020-06-02 01:45:09 +00:00
|
|
|
associated_file['name'] = '_'.join((extra_info['category1'],
|
|
|
|
approval_file.file_data.file_model.name))
|
2020-06-01 00:16:42 +00:00
|
|
|
associated_file['description'] = extra_info['description']
|
2020-05-31 23:24:23 +00:00
|
|
|
else:
|
|
|
|
associated_file['name'] = approval_file.file_data.file_model.name
|
|
|
|
associated_file['description'] = 'No description available'
|
2020-06-02 01:45:09 +00:00
|
|
|
associated_file['name'] = '(' + model.study.primary_investigator_id + ')' + associated_file['name']
|
2020-05-29 09:04:18 +00:00
|
|
|
associated_file['content_type'] = approval_file.file_data.file_model.content_type
|
2020-05-24 05:53:48 +00:00
|
|
|
instance.associated_files.append(associated_file)
|
|
|
|
|
|
|
|
return instance
|
|
|
|
|
2020-05-27 18:06:32 +00:00
|
|
|
def update_model(self, approval_model: ApprovalModel):
|
|
|
|
approval_model.status = self.status
|
|
|
|
approval_model.message = self.message
|
2020-05-25 21:40:24 +00:00
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
|
|
|
|
class ApprovalSchema(ma.Schema):
|
2020-06-02 22:17:00 +00:00
|
|
|
|
|
|
|
approver = fields.Nested(LdapSchema, dump_only=True)
|
|
|
|
primary_investigator = fields.Nested(LdapSchema, dump_only=True)
|
2020-06-02 23:36:06 +00:00
|
|
|
related_approvals = fields.List(fields.Nested('ApprovalSchema', allow_none=True, dump_only=True))
|
2020-06-02 22:17:00 +00:00
|
|
|
|
2020-05-22 22:25:00 +00:00
|
|
|
class Meta:
|
|
|
|
model = Approval
|
2020-05-24 05:53:48 +00:00
|
|
|
fields = ["id", "study_id", "workflow_id", "version", "title",
|
2020-06-02 11:43:19 +00:00
|
|
|
"status", "message", "approver", "primary_investigator",
|
2020-06-02 22:17:00 +00:00
|
|
|
"associated_files", "date_created", "date_approved",
|
|
|
|
"related_approvals"]
|
2020-05-22 22:25:00 +00:00
|
|
|
unknown = INCLUDE
|
|
|
|
|
2020-05-25 21:40:24 +00:00
|
|
|
@marshmallow.post_load
|
|
|
|
def make_approval(self, data, **kwargs):
|
|
|
|
"""Loads the basic approval data for updates to the database"""
|
|
|
|
return Approval(**data)
|
2020-05-24 05:53:48 +00:00
|
|
|
|
2020-06-02 22:17:00 +00:00
|
|
|
|