Merge branch 'convert-timestamp-499' into email-attachment-error-503

# Conflicts:
#	tests/emails/test_email_script.py
This commit is contained in:
mike cullerton 2021-10-18 17:08:56 -04:00
commit d84625c51e
11 changed files with 218 additions and 17 deletions

View File

@ -96,11 +96,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)

View File

@ -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"]

View File

@ -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
@ -93,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 = []

View File

@ -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.')

View File

@ -47,9 +47,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()

View File

@ -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")

View File

@ -19,15 +19,12 @@ Email content to be delivered to {{ ApprvlApprvr1 }}
---
**Test Some Formatting**
 _UVA Tracking Number:_ {{ 321 }}
</bpmn:documentation>
&amp;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" />

View File

@ -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>

View File

@ -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,8 +37,37 @@ 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
@ -46,7 +77,6 @@ class TestEmailScript(BaseTest):
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

View File

@ -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])

View File

@ -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))