Modified email script and email_service to accommodate new bcc, reply_to, and attachments arguments

Modified the email script description to add the new arguments
Cleaned up some import statements
This commit is contained in:
mike cullerton 2021-08-19 17:34:55 -04:00
parent ef9fd9514d
commit 32c72c5a40
2 changed files with 79 additions and 22 deletions

View File

@ -1,29 +1,42 @@
import sys import sys
import traceback import traceback
from crc import app from crc import app, session
from crc.api.common import ApiError from crc.api.common import ApiError
from crc.models.file import FileModel, CONTENT_TYPES
from crc.models.workflow import WorkflowModel
from crc.services.document_service import DocumentService
from crc.scripts.script import Script from crc.scripts.script import Script
from crc.services.ldap_service import LdapService
from crc.services.email_service import EmailService from crc.services.email_service import EmailService
from crc.services.ldap_service import LdapService
from crc.services.study_service import StudyService from crc.services.study_service import StudyService
class Email(Script): class Email(Script):
"""This Script allows to be introduced as part of a workflow and called from there, specifying """Send an email from a script task, as part of a workflow.
recipients and content """ You must specify recipients and content.
You can also specify cc, bcc, reply_to, and attachments"""
def get_description(self): def get_description(self):
return """ return """
Creates an email, using the provided `subject`, `recipients`, and `cc` arguments. Creates an email, using the provided `subject` and `recipients` arguments, which are required.
The recipients and cc arguments can contain an email address or list of email addresses. The `Element Documentation` field in the script task must contain markdown that becomes the body of the email message.
You can also provide `cc`, `bcc`, `reply_to` and `attachments` arguments.
The cc, bcc, reply_to, and attachments arguments are not required.
The recipients, cc, and bcc arguments can contain an email address or list of email addresses.
In place of an email address, we accept the string 'associated', in which case we In place of an email address, we accept the string 'associated', in which case we
look up the users associated with the study who have send_email set to True. look up the users associated with the study who have send_email set to True.
The cc argument is not required. The reply_to argument can contain an email address.
The "documentation" should contain markdown that will become the body of the email message. The attachments arguments can contain a doc_code or list of doc_codes.
Examples: Examples:
email (subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email, 'associated']) email(subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email, 'associated'])
email (subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email], cc='associated') email(subject="My Subject", recipients="user@example.com", cc='associated', bcc='test_user@example.com)
email(subject="My Subject", recipients="user@example.com", reply_to="reply_to@example.com")
email(subject="My Subject", recipients="user@example.com", attachments='Study_App_Doc')
email(subject="My Subject", recipients="user@example.com", attachments=['Study_App_Doc','Study_Protocol_Document'])
""" """
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
@ -37,8 +50,17 @@ email (subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email], cc='as
subject = self.get_subject(kwargs['subject']) subject = self.get_subject(kwargs['subject'])
recipients = self.get_email_addresses(kwargs['recipients'], study_id) recipients = self.get_email_addresses(kwargs['recipients'], study_id)
cc = [] cc = []
bcc = []
reply_to = None
files = None
if 'cc' in kwargs and kwargs['cc'] is not None: if 'cc' in kwargs and kwargs['cc'] is not None:
cc = self.get_email_addresses(kwargs['cc'], study_id) cc = self.get_email_addresses(kwargs['cc'], study_id)
if 'bcc' in kwargs and kwargs['bcc'] is not None:
bcc = self.get_email_addresses(kwargs['bcc'], study_id)
if 'reply_to' in kwargs:
reply_to = kwargs['reply_to']
if 'attachments' in kwargs:
files = self.get_files(kwargs['attachments'], study_id)
else: else:
raise ApiError(code="missing_argument", raise ApiError(code="missing_argument",
@ -56,7 +78,10 @@ email (subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email], cc='as
content=content, content=content,
content_html=content_html, content_html=content_html,
cc=cc, cc=cc,
study_id=study_id bcc=bcc,
study_id=study_id,
reply_to=reply_to,
attachment_files=files
) )
except Exception as e: except Exception as e:
exc_type, exc_value, exc_traceback = sys.exc_info() exc_type, exc_value, exc_traceback = sys.exc_info()
@ -118,3 +143,31 @@ email (subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email], cc='as
user_info = LdapService.user_info(associate.uid) user_info = LdapService.user_info(associate.uid)
associated_emails.append(user_info.email_address) associated_emails.append(user_info.email_address)
return associated_emails return associated_emails
@staticmethod
def get_files(attachments, study_id):
files = []
codes = None
if isinstance(attachments, str):
codes = [attachments]
elif isinstance(attachments, list):
codes = attachments
if codes is not None:
for code in codes:
if DocumentService.is_allowed_document(code):
workflows = session.query(WorkflowModel).filter(WorkflowModel.study_id==study_id).all()
for workflow in workflows:
workflow_files = session.query(FileModel).\
filter(FileModel.workflow_id == workflow.id).\
filter(FileModel.irb_doc_code == code).all()
for file in workflow_files:
files.append({'id': file.id, 'name': file.name, 'type': CONTENT_TYPES[file.type.value]})
else:
raise ApiError(code='bad_doc_code',
message=f'The doc_code {code} is not valid.')
else:
raise ApiError(code='bad_argument_type',
message='The attachments argument must be a string or list of strings')
return files

View File

@ -1,25 +1,23 @@
import markdown import markdown
import re import re
from datetime import datetime from flask import render_template
from flask import render_template, request
from flask_mail import Message from flask_mail import Message
from jinja2 import Template from jinja2 import Template
from sqlalchemy import desc
from crc import app, db, mail, session from crc import app, db, mail, session
from crc.api.common import ApiError
from crc.models.study import StudyModel
from crc.models.email import EmailModel from crc.models.email import EmailModel
from crc.models.file import FileDataModel
from crc.models.study import StudyModel
class EmailService(object): class EmailService(object):
"""Provides common tools for working with an Email""" """Provides common tools for working with an Email"""
@staticmethod @staticmethod
def add_email(subject, sender, recipients, content, content_html, cc=None, study_id=None): def add_email(subject, sender, recipients, content, content_html,
cc=None, bcc=None, study_id=None, reply_to=None, attachment_files=None):
"""We will receive all data related to an email and store it""" """We will receive all data related to an email and store it"""
# Find corresponding study - if any # Find corresponding study - if any
@ -35,11 +33,17 @@ class EmailService(object):
try: try:
msg = Message(subject, msg = Message(subject,
sender=sender, sender=sender,
recipients=recipients) recipients=recipients,
body=content,
html=content_html,
cc=cc,
bcc=bcc,
reply_to=reply_to)
msg.body = content if attachment_files is not None:
msg.html = content_html for file in attachment_files:
msg.cc = cc file_data = session.query(FileDataModel).filter(FileDataModel.file_model_id==file['id']).first()
msg.attach(file['name'], file['type'], file_data.data)
mail.send(msg) mail.send(msg)
except Exception as e: except Exception as e: