2021-07-22 20:02:08 +00:00
|
|
|
import sys
|
|
|
|
import traceback
|
|
|
|
|
2021-08-19 21:34:55 +00:00
|
|
|
from crc import app, session
|
2020-06-17 00:42:36 +00:00
|
|
|
from crc.api.common import ApiError
|
2021-10-13 15:41:44 +00:00
|
|
|
from crc.models.email import EmailModelSchema
|
2021-08-19 21:34:55 +00:00
|
|
|
from crc.models.file import FileModel, CONTENT_TYPES
|
|
|
|
from crc.models.workflow import WorkflowModel
|
|
|
|
from crc.services.document_service import DocumentService
|
2020-06-17 00:42:36 +00:00
|
|
|
from crc.scripts.script import Script
|
2021-01-26 14:19:28 +00:00
|
|
|
from crc.services.email_service import EmailService
|
2021-08-19 21:34:55 +00:00
|
|
|
from crc.services.ldap_service import LdapService
|
2021-03-09 11:49:39 +00:00
|
|
|
from crc.services.study_service import StudyService
|
2021-01-26 14:19:28 +00:00
|
|
|
|
2020-06-17 00:42:36 +00:00
|
|
|
|
|
|
|
class Email(Script):
|
2021-08-19 21:34:55 +00:00
|
|
|
"""Send an email from a script task, as part of a workflow.
|
|
|
|
You must specify recipients and content.
|
|
|
|
You can also specify cc, bcc, reply_to, and attachments"""
|
2020-06-17 00:42:36 +00:00
|
|
|
|
|
|
|
def get_description(self):
|
2021-09-22 16:12:26 +00:00
|
|
|
return """Creates an email, using the provided `subject` and `recipients` arguments, which are required.
|
2021-08-19 21:34:55 +00:00
|
|
|
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.
|
2021-03-11 16:32:21 +00:00
|
|
|
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.
|
2021-08-19 21:34:55 +00:00
|
|
|
The reply_to argument can contain an email address.
|
|
|
|
The attachments arguments can contain a doc_code or list of doc_codes.
|
|
|
|
|
2021-03-11 16:32:21 +00:00
|
|
|
Examples:
|
2021-08-19 21:34:55 +00:00
|
|
|
email(subject="My Subject", recipients=["dhf8r@virginia.edu", pi.email, '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'])
|
2020-06-17 00:42:36 +00:00
|
|
|
"""
|
|
|
|
|
2021-02-11 20:36:12 +00:00
|
|
|
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
|
2021-03-09 11:49:39 +00:00
|
|
|
self.get_subject(kwargs['subject'])
|
2021-03-11 16:32:21 +00:00
|
|
|
self.get_email_addresses(kwargs['recipients'], study_id)
|
|
|
|
EmailService().get_rendered_content(task.task_spec.documentation, task.data)
|
2020-06-17 00:42:36 +00:00
|
|
|
|
2020-12-29 23:05:13 +00:00
|
|
|
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
|
2020-12-09 17:11:46 +00:00
|
|
|
|
2021-03-09 11:49:39 +00:00
|
|
|
if 'subject' in kwargs and 'recipients' in kwargs:
|
|
|
|
subject = self.get_subject(kwargs['subject'])
|
2021-03-11 16:32:21 +00:00
|
|
|
recipients = self.get_email_addresses(kwargs['recipients'], study_id)
|
|
|
|
cc = []
|
2021-08-19 21:34:55 +00:00
|
|
|
bcc = []
|
|
|
|
reply_to = None
|
|
|
|
files = None
|
2021-03-11 16:32:21 +00:00
|
|
|
if 'cc' in kwargs and kwargs['cc'] is not None:
|
|
|
|
cc = self.get_email_addresses(kwargs['cc'], study_id)
|
2021-08-19 21:34:55 +00:00
|
|
|
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)
|
2021-03-09 11:49:39 +00:00
|
|
|
|
|
|
|
else:
|
2020-12-29 23:05:13 +00:00
|
|
|
raise ApiError(code="missing_argument",
|
2021-03-11 16:32:21 +00:00
|
|
|
message="Email script requires a subject and at least one email recipient as arguments")
|
2021-01-26 14:19:28 +00:00
|
|
|
|
2020-06-17 00:42:36 +00:00
|
|
|
if recipients:
|
2021-10-12 17:43:29 +00:00
|
|
|
wf_model = session.query(WorkflowModel).filter(WorkflowModel.id == workflow_id).first()
|
|
|
|
workflow_spec_id = wf_model.workflow_spec_id
|
2021-03-11 16:32:21 +00:00
|
|
|
message = task.task_spec.documentation
|
|
|
|
data = task.data
|
2021-07-22 20:02:08 +00:00
|
|
|
try:
|
|
|
|
content, content_html = EmailService().get_rendered_content(message, data)
|
2021-10-07 13:20:53 +00:00
|
|
|
email_model = EmailService.add_email(
|
2021-07-22 20:02:08 +00:00
|
|
|
subject=subject,
|
|
|
|
sender=app.config['DEFAULT_SENDER'],
|
|
|
|
recipients=recipients,
|
|
|
|
content=content,
|
|
|
|
content_html=content_html,
|
|
|
|
cc=cc,
|
2021-08-19 21:34:55 +00:00
|
|
|
bcc=bcc,
|
|
|
|
study_id=study_id,
|
|
|
|
reply_to=reply_to,
|
2021-10-07 13:20:53 +00:00
|
|
|
attachment_files=files,
|
2021-10-12 17:43:29 +00:00
|
|
|
workflow_spec_id=workflow_spec_id
|
2021-07-22 20:02:08 +00:00
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
|
|
print("*** format_exception:")
|
|
|
|
# exc_type below is ignored on 3.5 and later
|
|
|
|
print(repr(traceback.format_exception(exc_type, exc_value,
|
|
|
|
exc_traceback)))
|
|
|
|
raise e
|
2021-10-13 15:41:44 +00:00
|
|
|
return EmailModelSchema().dump(email_model)
|
2020-06-17 00:42:36 +00:00
|
|
|
|
2021-03-11 16:32:21 +00:00
|
|
|
def get_email_addresses(self, users, study_id):
|
2020-12-07 21:23:41 +00:00
|
|
|
emails = []
|
2021-03-11 16:32:21 +00:00
|
|
|
emails_to_check = []
|
2020-12-07 21:23:41 +00:00
|
|
|
|
2021-03-09 11:49:39 +00:00
|
|
|
# Recipient can be an email address or list of email addresses
|
2021-03-11 16:32:21 +00:00
|
|
|
# We also accept the string 'associated', in which case we lookup
|
|
|
|
# all users associated with a study who have send_email set to True
|
|
|
|
if isinstance(users, str):
|
|
|
|
if users == 'associated':
|
|
|
|
associated_emails = self.get_associated_emails(study_id)
|
|
|
|
for email in associated_emails:
|
|
|
|
emails_to_check.append(email)
|
|
|
|
else:
|
|
|
|
emails_to_check.append(users)
|
|
|
|
elif isinstance(users, list):
|
|
|
|
for user in users:
|
|
|
|
if user == 'associated':
|
|
|
|
associated_emails = self.get_associated_emails(study_id)
|
|
|
|
for email in associated_emails:
|
|
|
|
emails_to_check.append(email)
|
|
|
|
else:
|
|
|
|
emails_to_check.append(user)
|
2021-03-09 11:49:39 +00:00
|
|
|
else:
|
|
|
|
raise ApiError(code="invalid_argument",
|
2021-03-11 16:32:21 +00:00
|
|
|
message=f"Email script requires a valid email address (or list of addresses), but we received '{users}'")
|
2021-03-09 11:49:39 +00:00
|
|
|
|
|
|
|
for e in emails_to_check:
|
2021-03-11 16:32:21 +00:00
|
|
|
if EmailService().check_valid_email(e):
|
2021-03-09 11:49:39 +00:00
|
|
|
emails.append(e)
|
2020-12-07 21:23:41 +00:00
|
|
|
else:
|
2020-06-17 00:42:36 +00:00
|
|
|
raise ApiError(code="invalid_argument",
|
2021-03-09 11:49:39 +00:00
|
|
|
message="The email address you provided could not be parsed. "
|
|
|
|
"The value you provided is '%s" % e)
|
2020-12-29 23:05:13 +00:00
|
|
|
return emails
|
2020-06-17 00:42:36 +00:00
|
|
|
|
2021-01-26 14:19:28 +00:00
|
|
|
@staticmethod
|
2021-03-09 11:49:39 +00:00
|
|
|
def get_subject(subject):
|
2020-12-09 17:11:46 +00:00
|
|
|
if not subject or not isinstance(subject, str):
|
2020-06-18 03:11:47 +00:00
|
|
|
raise ApiError(code="invalid_argument",
|
2020-12-09 17:11:46 +00:00
|
|
|
message="The subject you provided could not be parsed. "
|
|
|
|
"The value is \"%s\" " % subject)
|
2020-06-18 18:53:50 +00:00
|
|
|
return subject
|
2020-06-17 00:42:36 +00:00
|
|
|
|
2021-01-27 22:12:03 +00:00
|
|
|
@staticmethod
|
2021-03-11 16:32:21 +00:00
|
|
|
def get_associated_emails(study_id):
|
|
|
|
associated_emails = []
|
|
|
|
associates = StudyService.get_study_associates(study_id)
|
|
|
|
for associate in associates:
|
2021-08-10 20:16:08 +00:00
|
|
|
if associate.send_email is True:
|
|
|
|
user_info = LdapService.user_info(associate.uid)
|
2021-03-15 14:30:23 +00:00
|
|
|
associated_emails.append(user_info.email_address)
|
2021-03-11 16:32:21 +00:00
|
|
|
return associated_emails
|
2021-08-19 21:34:55 +00:00
|
|
|
|
|
|
|
@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
|