Merge branch 'dev' into jinja-include-403
This commit is contained in:
commit
1c02928d04
|
@ -47,6 +47,7 @@ ma = Marshmallow(app)
|
|||
from crc import models
|
||||
from crc import api
|
||||
from crc.api import admin
|
||||
from crc.services.file_service import FileService
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
connexion_app.add_api('api.yml', base_path='/v1.0')
|
||||
|
||||
|
@ -57,6 +58,7 @@ def process_waiting_tasks():
|
|||
WorkflowService.do_waiting()
|
||||
|
||||
scheduler.add_job(process_waiting_tasks,'interval',minutes=1)
|
||||
scheduler.add_job(FileService.cleanup_file_data, 'interval', minutes=1440) # once a day
|
||||
scheduler.start()
|
||||
|
||||
|
||||
|
|
|
@ -102,11 +102,12 @@ def evaluate_python_expression(body):
|
|||
except Exception as e:
|
||||
return {"result": False, "expression": body['expression'], "key": body['key'], "error": str(e)}
|
||||
|
||||
|
||||
def send_test_email(subject, address, message, data=None):
|
||||
rendered, wrapped = EmailService().get_rendered_content(message, data)
|
||||
content, content_html = EmailService().get_rendered_content(message, data)
|
||||
EmailService.add_email(
|
||||
subject=subject,
|
||||
sender=DEFAULT_SENDER,
|
||||
recipients=[address],
|
||||
content=rendered,
|
||||
content_html=wrapped)
|
||||
content=content,
|
||||
content_html=content_html)
|
||||
|
|
|
@ -23,9 +23,8 @@ class EmailModel(db.Model):
|
|||
|
||||
|
||||
class EmailModelSchema(ma.Schema):
|
||||
# TODO: clean this us. Do we need load_instance and unknown?
|
||||
|
||||
class Meta:
|
||||
model = EmailModel
|
||||
load_instance = True
|
||||
additional = ["id", "subject", "sender", "recipients", "timestamp"]
|
||||
unknown = INCLUDE
|
||||
fields = ["id", "subject", "sender", "recipients", "cc", "bcc", "content", "content_html",
|
||||
"study_id", "timestamp", "workflow_spec_id"]
|
||||
|
|
|
@ -3,6 +3,7 @@ import traceback
|
|||
|
||||
from crc import app, session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.email import EmailModelSchema
|
||||
from crc.models.file import FileModel, CONTENT_TYPES
|
||||
from crc.models.workflow import WorkflowModel
|
||||
from crc.services.document_service import DocumentService
|
||||
|
@ -58,7 +59,8 @@ email(subject="My Subject", recipients="user@example.com", attachments=['Study_A
|
|||
bcc = self.get_email_addresses(kwargs['bcc'], study_id)
|
||||
if 'reply_to' in kwargs:
|
||||
reply_to = kwargs['reply_to']
|
||||
if 'attachments' in kwargs:
|
||||
# Don't process if attachments is None or ''
|
||||
if 'attachments' in kwargs and kwargs['attachments'] is not None and kwargs['attachments'] != '':
|
||||
files = self.get_files(kwargs['attachments'], study_id)
|
||||
|
||||
else:
|
||||
|
@ -92,7 +94,7 @@ email(subject="My Subject", recipients="user@example.com", attachments=['Study_A
|
|||
print(repr(traceback.format_exception(exc_type, exc_value,
|
||||
exc_traceback)))
|
||||
raise e
|
||||
return email_model.id
|
||||
return EmailModelSchema().dump(email_model)
|
||||
|
||||
def get_email_addresses(self, users, study_id):
|
||||
emails = []
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
from crc.api.common import ApiError
|
||||
from crc.scripts.script import Script
|
||||
|
||||
import dateparser
|
||||
import pytz
|
||||
|
||||
|
||||
class GetLocaltime(Script):
|
||||
|
||||
def get_description(self):
|
||||
return """Converts a UTC Datetime object into a Datetime object with a different timezone.
|
||||
Defaults to US/Eastern"""
|
||||
|
||||
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
if 'timestamp' in kwargs:
|
||||
return True
|
||||
raise ApiError(code='missing_timestamp',
|
||||
message='You must include a timestamp to convert.')
|
||||
|
||||
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
if 'timestamp' in kwargs:
|
||||
timestamp = kwargs['timestamp']
|
||||
if 'timezone' in kwargs:
|
||||
timezone = kwargs['timezone']
|
||||
else:
|
||||
timezone = 'US/Eastern'
|
||||
parsed_timestamp = dateparser.parse(timestamp)
|
||||
localtime = parsed_timestamp.astimezone(pytz.timezone(timezone))
|
||||
return str(localtime)
|
||||
|
||||
else:
|
||||
raise ApiError(code='missing_timestamp',
|
||||
message='You must include a timestamp to convert.')
|
||||
|
|
@ -49,9 +49,11 @@ class EmailService(object):
|
|||
msg.attach(file['name'], file['type'], file_data.data)
|
||||
|
||||
mail.send(msg)
|
||||
|
||||
except Exception as e:
|
||||
app.logger.error('An exception happened in EmailService', exc_info=True)
|
||||
app.logger.error(str(e))
|
||||
raise e
|
||||
|
||||
db.session.add(email_model)
|
||||
db.session.commit()
|
||||
|
|
|
@ -545,3 +545,49 @@ class FileService(object):
|
|||
dmn_file = prefix + etree.tostring(root)
|
||||
|
||||
return dmn_file
|
||||
|
||||
@staticmethod
|
||||
def cleanup_file_data(copies_to_keep=1):
|
||||
if isinstance(copies_to_keep, int) and copies_to_keep > 0:
|
||||
|
||||
deleted_models = []
|
||||
saved_models = []
|
||||
current_models = []
|
||||
|
||||
session.flush()
|
||||
|
||||
workflow_spec_models = session.query(WorkflowSpecModel).all()
|
||||
|
||||
for wf_spec_model in workflow_spec_models:
|
||||
file_models = session.query(FileModel)\
|
||||
.filter(FileModel.workflow_spec_id == wf_spec_model.id)\
|
||||
.all()
|
||||
|
||||
for file_model in file_models:
|
||||
file_data_models = session.query(FileDataModel)\
|
||||
.filter(FileDataModel.file_model_id == file_model.id)\
|
||||
.order_by(desc(FileDataModel.date_created))\
|
||||
.all()
|
||||
current_models.append(file_data_models[:copies_to_keep])
|
||||
for fd_model in file_data_models[copies_to_keep:]:
|
||||
dependencies = session.query(WorkflowSpecDependencyFile)\
|
||||
.filter(WorkflowSpecDependencyFile.file_data_id == fd_model.id)\
|
||||
.all()
|
||||
if len(dependencies) > 0:
|
||||
saved_models.append(fd_model)
|
||||
continue
|
||||
lookups = session.query(LookupFileModel)\
|
||||
.filter(LookupFileModel.file_data_model_id == fd_model.id)\
|
||||
.all()
|
||||
if len(lookups) > 0:
|
||||
saved_models.append(fd_model)
|
||||
continue
|
||||
deleted_models.append(fd_model)
|
||||
session.delete(fd_model)
|
||||
|
||||
session.commit()
|
||||
return current_models, saved_models, deleted_models
|
||||
|
||||
else:
|
||||
raise ApiError(code='bad_keep',
|
||||
message='You must keep at least 1 version')
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
"""modify email timestamp
|
||||
|
||||
Revision ID: ba6df7e560a1
|
||||
Revises: 6d8ceb1c18cb
|
||||
Create Date: 2021-10-13 10:54:23.894034
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ba6df7e560a1'
|
||||
down_revision = '6d8ceb1c18cb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.execute("alter table email alter column timestamp type timestamp with time zone")
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.execute("alter table email alter column timestamp type timestamp without time zone")
|
|
@ -19,15 +19,12 @@ Email content to be delivered to {{ ApprvlApprvr1 }}
|
|||
|
||||
---
|
||||
**Test Some Formatting**
|
||||
 _UVA Tracking Number:_ {{ 321 }}
|
||||
|
||||
</bpmn:documentation>
|
||||
&ensp;_UVA Tracking Number:_ {{ 321 }}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_08n2npe</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1xlrgne</bpmn:outgoing>
|
||||
<bpmn:script>subject="Camunda Email Subject"
|
||||
recipients=[ApprvlApprvr1,PIComputingID]
|
||||
email_id = email(subject=subject,recipients=recipients)
|
||||
</bpmn:script>
|
||||
email_model = email(subject=subject,recipients=recipients)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1synsig" sourceRef="StartEvent_1" targetRef="Activity_1l9vih3" />
|
||||
<bpmn:sequenceFlow id="Flow_1xlrgne" sourceRef="Activity_0s5v97n" targetRef="Event_0izrcj4" />
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0y2dq4f" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_0tad5ma" name="Set Recipients" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_1synsig</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:scriptTask id="Activity_SendEmail" name="Send Email">
|
||||
<bpmn:documentation># Dear Approver
|
||||
## you have been requested for approval
|
||||
|
||||
|
||||
---
|
||||
New request submitted by {{ PIComputingID }}
|
||||
|
||||
Email content to be delivered to {{ ApprvlApprvr1 }}
|
||||
|
||||
---
|
||||
**Test Some Formatting**
|
||||
&ensp;_UVA Tracking Number:_ {{ 321 }}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_08n2npe</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1ch3gt4</bpmn:outgoing>
|
||||
<bpmn:script>attachments = ''
|
||||
email_id = email(subject=subject,recipients=recipients, attachments=attachments)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1synsig" sourceRef="StartEvent_1" targetRef="Activity_GetEmailData" />
|
||||
<bpmn:sequenceFlow id="Flow_08n2npe" sourceRef="Activity_GetEmailData" targetRef="Activity_SendEmail" />
|
||||
<bpmn:userTask id="Activity_GetEmailData" name="Get Email Data" camunda:formKey="MyFormKey">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="subject" label="Subject" type="string" />
|
||||
<camunda:formField id="recipients" label="Recipients" type="string" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_1synsig</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_08n2npe</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_1ch3gt4" sourceRef="Activity_SendEmail" targetRef="Activity_DisplayEmail" />
|
||||
<bpmn:endEvent id="Event_12vfpx3">
|
||||
<bpmn:incoming>Flow_1gei5cf</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1gei5cf" sourceRef="Activity_DisplayEmail" targetRef="Event_12vfpx3" />
|
||||
<bpmn:manualTask id="Activity_DisplayEmail" name="Display Emai">
|
||||
<bpmn:documentation># Email
|
||||
{{ email_id }}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_1ch3gt4</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1gei5cf</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0tad5ma">
|
||||
<bpmndi:BPMNEdge id="Flow_1gei5cf_di" bpmnElement="Flow_1gei5cf">
|
||||
<di:waypoint x="730" y="117" />
|
||||
<di:waypoint x="812" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1ch3gt4_di" bpmnElement="Flow_1ch3gt4">
|
||||
<di:waypoint x="550" y="117" />
|
||||
<di:waypoint x="630" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_08n2npe_di" bpmnElement="Flow_08n2npe">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="450" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1synsig_di" bpmnElement="Flow_1synsig">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_04imfm6_di" bpmnElement="Activity_SendEmail">
|
||||
<dc:Bounds x="450" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0xugr62_di" bpmnElement="Activity_GetEmailData">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_12vfpx3_di" bpmnElement="Event_12vfpx3">
|
||||
<dc:Bounds x="812" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0intlt7_di" bpmnElement="Activity_DisplayEmail">
|
||||
<dc:Bounds x="630" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0y2dq4f" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_0tad5ma" name="Set Recipients" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_1synsig</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:scriptTask id="Activity_SendEmail" name="Send Email">
|
||||
<bpmn:documentation># Dear Approver
|
||||
## you have been requested for approval
|
||||
|
||||
|
||||
---
|
||||
New request submitted by {{ PIComputingID }}
|
||||
|
||||
Email content to be delivered to {{ ApprvlApprvr1 }}
|
||||
|
||||
---
|
||||
**Test Some Formatting**
|
||||
&ensp;_UVA Tracking Number:_ {{ 321 }}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_08n2npe</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1ch3gt4</bpmn:outgoing>
|
||||
<bpmn:script>attachments = None
|
||||
email_id = email(subject=subject,recipients=recipients, attachments=attachments)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1synsig" sourceRef="StartEvent_1" targetRef="Activity_GetEmailData" />
|
||||
<bpmn:sequenceFlow id="Flow_08n2npe" sourceRef="Activity_GetEmailData" targetRef="Activity_SendEmail" />
|
||||
<bpmn:userTask id="Activity_GetEmailData" name="Get Email Data" camunda:formKey="MyFormKey">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="subject" label="Subject" type="string" />
|
||||
<camunda:formField id="recipients" label="Recipients" type="string" />
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_1synsig</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_08n2npe</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:sequenceFlow id="Flow_1ch3gt4" sourceRef="Activity_SendEmail" targetRef="Activity_DisplayEmail" />
|
||||
<bpmn:endEvent id="Event_12vfpx3">
|
||||
<bpmn:incoming>Flow_1gei5cf</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_1gei5cf" sourceRef="Activity_DisplayEmail" targetRef="Event_12vfpx3" />
|
||||
<bpmn:manualTask id="Activity_DisplayEmail" name="Display Emai">
|
||||
<bpmn:documentation># Email
|
||||
{{ email_id }}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_1ch3gt4</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1gei5cf</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0tad5ma">
|
||||
<bpmndi:BPMNEdge id="Flow_1gei5cf_di" bpmnElement="Flow_1gei5cf">
|
||||
<di:waypoint x="730" y="117" />
|
||||
<di:waypoint x="812" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1ch3gt4_di" bpmnElement="Flow_1ch3gt4">
|
||||
<di:waypoint x="550" y="117" />
|
||||
<di:waypoint x="630" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_08n2npe_di" bpmnElement="Flow_08n2npe">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="450" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1synsig_di" bpmnElement="Flow_1synsig">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_04imfm6_di" bpmnElement="Activity_SendEmail">
|
||||
<dc:Bounds x="450" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0xugr62_di" bpmnElement="Activity_GetEmailData">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_12vfpx3_di" bpmnElement="Event_12vfpx3">
|
||||
<dc:Bounds x="812" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0intlt7_di" bpmnElement="Activity_DisplayEmail">
|
||||
<dc:Bounds x="630" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0szq8v9" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_1dxw783" name="Get Localtime" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0lnc9x0</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0lnc9x0" sourceRef="StartEvent_1" targetRef="Activity_0aq21yg" />
|
||||
<bpmn:sequenceFlow id="Flow_0gtgzcf" sourceRef="Activity_0aq21yg" targetRef="Activity_1by2ose" />
|
||||
<bpmn:sequenceFlow id="Flow_0k1hbif" sourceRef="Activity_1by2ose" targetRef="Activity_0d5fjpa" />
|
||||
<bpmn:endEvent id="Event_1vxo45i">
|
||||
<bpmn:incoming>Flow_0kgtoh1</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0kgtoh1" sourceRef="Activity_0d5fjpa" targetRef="Event_1vxo45i" />
|
||||
<bpmn:scriptTask id="Activity_0aq21yg" name="Send Email">
|
||||
<bpmn:documentation>This is my email</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0lnc9x0</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0gtgzcf</bpmn:outgoing>
|
||||
<bpmn:script>email_model = email(subject='My Email Subject', recipients='user@example.com')</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:scriptTask id="Activity_1by2ose" name="Get Localtime">
|
||||
<bpmn:documentation>timestamp = email_model.timestamp
|
||||
localtime = get_localtime(str(timestamp))</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0gtgzcf</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0k1hbif</bpmn:outgoing>
|
||||
<bpmn:script>timestamp=email_model.timestamp
|
||||
localtime = get_localtime(timestamp=timestamp)</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:manualTask id="Activity_0d5fjpa" name="Display Times">
|
||||
<bpmn:documentation># Timestamp
|
||||
{{ timestamp }}
|
||||
|
||||
|
||||
# Localtime
|
||||
{{ localtime }}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_0k1hbif</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0kgtoh1</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1dxw783">
|
||||
<bpmndi:BPMNEdge id="Flow_0lnc9x0_di" bpmnElement="Flow_0lnc9x0">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0gtgzcf_di" bpmnElement="Flow_0gtgzcf">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="430" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0k1hbif_di" bpmnElement="Flow_0k1hbif">
|
||||
<di:waypoint x="530" y="117" />
|
||||
<di:waypoint x="590" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0kgtoh1_di" bpmnElement="Flow_0kgtoh1">
|
||||
<di:waypoint x="690" y="117" />
|
||||
<di:waypoint x="752" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_1vxo45i_di" bpmnElement="Event_1vxo45i">
|
||||
<dc:Bounds x="752" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_01qg6wo_di" bpmnElement="Activity_0aq21yg">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0q4ycxr_di" bpmnElement="Activity_1by2ose">
|
||||
<dc:Bounds x="430" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_178gn50_di" bpmnElement="Activity_0d5fjpa">
|
||||
<dc:Bounds x="590" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -1,6 +1,8 @@
|
|||
from tests.base_test import BaseTest
|
||||
from crc import mail
|
||||
from crc.models.email import EmailModel
|
||||
import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestEmailScript(BaseTest):
|
||||
|
@ -35,5 +37,78 @@ class TestEmailScript(BaseTest):
|
|||
# Correct From field
|
||||
self.assertEqual('uvacrconnect@virginia.edu', outbox[0].sender)
|
||||
|
||||
db_emails = EmailModel.query.count()
|
||||
self.assertEqual(db_emails, 1)
|
||||
# Make sure we log the email
|
||||
# Grab model for comparison below
|
||||
db_emails = EmailModel.query.all()
|
||||
self.assertEqual(len(db_emails), 1)
|
||||
|
||||
# Check whether we get a good email model back from the script
|
||||
self.assertIn('email_model', workflow_api.next_task.data)
|
||||
self.assertEqual(db_emails[0].recipients, workflow_api.next_task.data['email_model']['recipients'])
|
||||
self.assertEqual(db_emails[0].sender, workflow_api.next_task.data['email_model']['sender'])
|
||||
self.assertEqual(db_emails[0].subject, workflow_api.next_task.data['email_model']['subject'])
|
||||
|
||||
# Make sure timestamp is UTC
|
||||
self.assertEqual(db_emails[0].timestamp.tzinfo, datetime.timezone.utc)
|
||||
|
||||
@patch('crc.services.email_service.EmailService.add_email')
|
||||
def test_email_raises_exception(self, mock_response):
|
||||
self.load_example_data()
|
||||
mock_response.return_value.ok = True
|
||||
mock_response.side_effect = Exception("This is my exception!")
|
||||
|
||||
workflow = self.create_workflow('email')
|
||||
|
||||
task_data = {
|
||||
'PIComputingID': 'dhf8r@virginia.edu',
|
||||
'ApprvlApprvr1': 'lb3dp@virginia.edu'
|
||||
}
|
||||
task = self.get_workflow_api(workflow).next_task
|
||||
|
||||
with mail.record_messages() as outbox:
|
||||
with self.assertRaises(Exception) as e:
|
||||
self.complete_form(workflow, task, task_data)
|
||||
|
||||
def test_email_with_none_attachment(self):
|
||||
# This workflow specifically sends `attachments = None` as a parameter
|
||||
# to the email script
|
||||
workflow = self.create_workflow('email_none_attachment')
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
task = workflow_api.next_task
|
||||
|
||||
with mail.record_messages() as outbox:
|
||||
workflow_api = self.complete_form(workflow, task, {'subject': 'My Test Email',
|
||||
'recipients': 'user@example.com'})
|
||||
task = workflow_api.next_task
|
||||
# Make sure 'attachments' is in task.data, and is None
|
||||
self.assertIn('attachments', task.data)
|
||||
self.assertEqual(task.data['attachments'], None)
|
||||
|
||||
# Make sure we still send an email
|
||||
self.assertIn('email_id', task.data)
|
||||
|
||||
self.assertEqual(len(outbox), 1)
|
||||
self.assertEqual(outbox[0].subject, "My Test Email")
|
||||
self.assertEqual(outbox[0].recipients, ['user@example.com'])
|
||||
|
||||
def test_email_attachment_empty_string(self):
|
||||
# This workflow specifically sends `attachments = ''` as a parameter
|
||||
# to the email script
|
||||
workflow = self.create_workflow('email_attachment_empty_string')
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
task = workflow_api.next_task
|
||||
|
||||
with mail.record_messages() as outbox:
|
||||
workflow_api = self.complete_form(workflow, task, {'subject': 'My Test Email',
|
||||
'recipients': 'user@example.com'})
|
||||
task = workflow_api.next_task
|
||||
# Make sure 'attachments' is in task.data, and is None
|
||||
self.assertIn('attachments', task.data)
|
||||
self.assertEqual(task.data['attachments'], '')
|
||||
|
||||
# Make sure we still send an email
|
||||
self.assertIn('email_id', task.data)
|
||||
|
||||
self.assertEqual(len(outbox), 1)
|
||||
self.assertEqual(outbox[0].subject, "My Test Email")
|
||||
self.assertEqual(outbox[0].recipients, ['user@example.com'])
|
||||
|
|
|
@ -3,6 +3,7 @@ from tests.base_test import BaseTest
|
|||
from crc import session
|
||||
from crc.models.email import EmailModel
|
||||
from crc.services.email_service import EmailService
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestEmailService(BaseTest):
|
||||
|
@ -10,7 +11,6 @@ class TestEmailService(BaseTest):
|
|||
def test_add_email(self):
|
||||
self.load_example_data()
|
||||
study = self.create_study()
|
||||
workflow = self.create_workflow('random_fact')
|
||||
|
||||
subject = 'Email Subject'
|
||||
sender = 'sender@sartography.com'
|
||||
|
@ -42,3 +42,25 @@ class TestEmailService(BaseTest):
|
|||
self.assertEqual(email_model.content, content)
|
||||
self.assertEqual(email_model.content_html, content_html)
|
||||
self.assertEqual(email_model.study, None)
|
||||
|
||||
@patch('crc.services.email_service.EmailService.add_email')
|
||||
def test_add_email_with_error(self, mock_response):
|
||||
|
||||
self.load_example_data()
|
||||
mock_response.return_value.ok = True
|
||||
mock_response.side_effect = Exception("This is my exception!")
|
||||
|
||||
study = self.create_study()
|
||||
|
||||
subject = 'Email Subject'
|
||||
sender = 'sender@sartography.com'
|
||||
recipients = ['recipient@sartography.com', 'back@sartography.com']
|
||||
content = 'Content for this email'
|
||||
content_html = '<p>Hypertext Markup Language content for this email</p>'
|
||||
|
||||
# Make sure we generate an error
|
||||
with self.assertRaises(Exception) as e:
|
||||
EmailService.add_email(subject=subject, sender=sender, recipients=recipients,
|
||||
content=content, content_html=content_html, study_id=study.id)
|
||||
# Make sure it's the error we want
|
||||
self.assertEqual('This is my exception!', e.exception.args[0])
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
from tests.base_test import BaseTest
|
||||
|
||||
from crc import session
|
||||
from crc.models.file import FileModel, FileDataModel, LookupFileModel
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecDependencyFile
|
||||
from crc.services.file_service import FileService
|
||||
|
||||
from sqlalchemy import desc
|
||||
|
||||
import io
|
||||
import json
|
||||
|
||||
|
||||
class TestFileDataCleanup(BaseTest):
|
||||
|
||||
xml_str_one = b"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_0e68fp5" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||
<bpmn:process id="Process_054hyyv" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_054hyyv">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>"""
|
||||
|
||||
xml_str_two = b"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0e68fp5" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
|
||||
<bpmn:process id="Process_054hyyv" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_1v0s5ht</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:task id="Activity_08xcoa5" name="Say Hello">
|
||||
<bpmn:documentation># Hello</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_1v0s5ht</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_12k5ua1</bpmn:outgoing>
|
||||
</bpmn:task>
|
||||
<bpmn:sequenceFlow id="Flow_1v0s5ht" sourceRef="StartEvent_1" targetRef="Activity_08xcoa5" />
|
||||
<bpmn:endEvent id="Event_10ufcgd">
|
||||
<bpmn:incoming>Flow_12k5ua1</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_12k5ua1" sourceRef="Activity_08xcoa5" targetRef="Event_10ufcgd" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_054hyyv">
|
||||
<bpmndi:BPMNEdge id="Flow_1v0s5ht_di" bpmnElement="Flow_1v0s5ht">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_12k5ua1_di" bpmnElement="Flow_12k5ua1">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="432" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_08xcoa5_di" bpmnElement="Activity_08xcoa5">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_10ufcgd_di" bpmnElement="Event_10ufcgd">
|
||||
<dc:Bounds x="432" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
||||
"""
|
||||
|
||||
def test_file_data_cleanup(self):
|
||||
"""Update a file twice. Make sure we clean up the correct files"""
|
||||
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('empty_workflow')
|
||||
file_data_model_count = session.query(FileDataModel).count()
|
||||
|
||||
# Use for comparison after cleanup
|
||||
replaced_models = []
|
||||
|
||||
# Get `empty_workflow` workflow spec
|
||||
workflow_spec_model = session.query(WorkflowSpecModel)\
|
||||
.filter(WorkflowSpecModel.name == 'empty_workflow')\
|
||||
.first()
|
||||
|
||||
# Get file model for empty_workflow spec
|
||||
file_model = session.query(FileModel)\
|
||||
.filter(FileModel.workflow_spec_id == workflow_spec_model.id)\
|
||||
.first()
|
||||
|
||||
# Grab the file data model for empty_workflow file_model
|
||||
original_file_data_model = session.query(FileDataModel)\
|
||||
.filter(FileDataModel.file_model_id == file_model.id)\
|
||||
.order_by(desc(FileDataModel.date_created))\
|
||||
.first()
|
||||
|
||||
# Add file to dependencies
|
||||
# It should not get deleted
|
||||
wf_spec_depend_model = WorkflowSpecDependencyFile(file_data_id=original_file_data_model.id,
|
||||
workflow_id=workflow.id)
|
||||
session.add(wf_spec_depend_model)
|
||||
session.commit()
|
||||
|
||||
# Update first time
|
||||
replaced_models.append(original_file_data_model)
|
||||
data = {'file': (io.BytesIO(self.xml_str_one), file_model.name)}
|
||||
rv = self.app.put('/v1.0/file/%i/data' % file_model.id, data=data, follow_redirects=True,
|
||||
content_type='multipart/form-data', headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
file_json_first = json.loads(rv.get_data(as_text=True))
|
||||
|
||||
# Update second time
|
||||
# replaced_models.append(old_file_data_model)
|
||||
data = {'file': (io.BytesIO(self.xml_str_two), file_model.name)}
|
||||
rv = self.app.put('/v1.0/file/%i/data' % file_model.id, data=data, follow_redirects=True,
|
||||
content_type='multipart/form-data', headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
file_json_second = json.loads(rv.get_data(as_text=True))
|
||||
|
||||
# Add lookup file
|
||||
data = {'file': (io.BytesIO(b'asdf'), 'lookup_1.xlsx')}
|
||||
rv = self.app.post('/v1.0/file?workflow_spec_id=%s' % workflow_spec_model.id, data=data, follow_redirects=True,
|
||||
content_type='multipart/form-data', headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
file_json = json.loads(rv.get_data(as_text=True))
|
||||
lookup_file_id = file_json['id']
|
||||
lookup_data_model = session.query(FileDataModel).filter(FileDataModel.file_model_id == lookup_file_id).first()
|
||||
lookup_model = LookupFileModel(file_data_model_id=lookup_data_model.id,
|
||||
workflow_spec_id=workflow_spec_model.id)
|
||||
session.add(lookup_model)
|
||||
session.commit()
|
||||
|
||||
# Update lookup file
|
||||
data = {'file': (io.BytesIO(b'1234'), 'lookup_1.xlsx')}
|
||||
rv = self.app.put('/v1.0/file/%i/data' % lookup_file_id, data=data, follow_redirects=True,
|
||||
content_type='multipart/form-data', headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
|
||||
# Run the cleanup files process
|
||||
current_models, saved_models, deleted_models = FileService.cleanup_file_data()
|
||||
|
||||
# assert correct versions are removed
|
||||
new_count = session.query(FileDataModel).count()
|
||||
self.assertEqual(8, new_count)
|
||||
self.assertEqual(4, len(current_models))
|
||||
self.assertEqual(2, len(saved_models))
|
||||
self.assertEqual(1, len(deleted_models))
|
||||
|
||||
print('test_file_data_cleanup')
|
|
@ -0,0 +1,17 @@
|
|||
from tests.base_test import BaseTest
|
||||
from crc.scripts.get_localtime import GetLocaltime
|
||||
|
||||
|
||||
class TestGetLocaltime(BaseTest):
|
||||
|
||||
def test_get_localtime(self):
|
||||
self.load_example_data()
|
||||
|
||||
workflow = self.create_workflow('get_localtime')
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
task = workflow_api.next_task
|
||||
|
||||
timestamp = task.data['timestamp']
|
||||
localtime = task.data['localtime']
|
||||
|
||||
self.assertEqual(localtime, GetLocaltime().do_task(None, None, None, timestamp=timestamp))
|
Loading…
Reference in New Issue