Skip to content

Commit

Permalink
Merge bb8e1d5 into 65ebac7
Browse files Browse the repository at this point in the history
  • Loading branch information
dhakim87 committed Jul 9, 2020
2 parents 65ebac7 + bb8e1d5 commit 51a91de
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 11 deletions.
87 changes: 87 additions & 0 deletions microsetta_private_api/admin/admin_impl.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import uuid

import flask
from flask import jsonify
import datetime

from microsetta_private_api.admin.email_templates import EmailMessage
from microsetta_private_api.config_manager import SERVER_CONFIG
from microsetta_private_api.model.log_event import LogEvent
from microsetta_private_api.exceptions import RepoException
from microsetta_private_api.repo.account_repo import AccountRepo
from microsetta_private_api.repo.event_log_repo import EventLogRepo
from microsetta_private_api.repo.transaction import Transaction
from microsetta_private_api.repo.admin_repo import AdminRepo
from microsetta_private_api.util.email import SendEmail
from microsetta_private_api.util.redirects import build_login_redirect
from werkzeug.exceptions import Unauthorized, BadRequest


Expand Down Expand Up @@ -164,3 +172,82 @@ def create_kits(body, token_info):
t.commit()

return jsonify(kits), 201


def get_account_events(account_id, token_info):
validate_admin_access(token_info)

with Transaction() as t:
events = EventLogRepo(t).get_events_by_account(account_id)
return jsonify([x.to_api() for x in events]), 200


def send_email(body, token_info):
validate_admin_access(token_info)

with Transaction() as t:
account_id = None
email = None
resolution_url = None
contact_name = None

# Depending on issue type, determine what email to send to and
# what account is involved, as well as what link to send user to
# address the problem if that is required by the email template
if body["issue_type"] == "sample":
# TODO: Building resolution url is very tricky, and it's not clear
# what component should be responsible for doing it. It requires
# knowing what endpoint the client minimal interface is hosted at,
# as well as a sample barcode's associated
# account_id,
# source_id,
# sample_id
# which generally requires lookup in the db with admin privilege.

diag = AdminRepo(t).retrieve_diagnostics_by_barcode(
body["template_args"]["sample_barcode"],
grab_kit=False)
account_id = diag["account"].id
source_id = diag["source"].id
sample_id = diag["sample"].id
email = diag["account"].email
contact_name = diag["account"].first_name + " " + \
diag["account"].last_name
contact_name = contact_name.strip()
endpoint = SERVER_CONFIG["endpoint"]
resolution_url = build_login_redirect(
endpoint + "/accounts/%s/sources/%s/samples/%s" %
(account_id, source_id, sample_id)
)
else:
raise Exception("Update Admin Impl to support more issue types")

# Determine what template must be sent, and build the template args
# from whatever is in the body and the resolution url we determined
template = EmailMessage[body['template']]
template_args = dict(body['template_args'])
template_args['resolution_url'] = resolution_url
template_args['contact_name'] = contact_name

# Send the email
SendEmail.send(email, template, template_args)

# Add an event to the log that we sent this email successfully
event = LogEvent(
uuid.uuid4(),
template.event_type,
template.event_subtype,
None,
{
# account_id and email are necessary to allow searching the
# event log.
"account_id": account_id,
"email": email,
"template": body["template"],
"template_args": body["template_args"]
})
EventLogRepo(t).add_event(event)

t.commit()

return '', 204
43 changes: 43 additions & 0 deletions microsetta_private_api/admin/email_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from enum import Enum
from flask import render_template
from werkzeug.exceptions import BadRequest
from microsetta_private_api.model.log_event import EventType, EventSubtype


class EmailTemplate:
def __init__(self, filepath, required):
self.filepath = filepath
self.required = required

def render(self, template_args):
for key in self.required:
if key not in template_args:
raise BadRequest("Missing Required Argument: " + str(key))
return render_template(self.filepath, **template_args)


class EmailMessage(Enum):
incorrect_sample_type = (
"Incorrect Sample Type",
"email/incorrect_sample_type.jinja2",
"email/incorrect_sample_type.plain",
("contact_name", "sample_barcode", "recorded_type", "received_type",
"resolution_url"),
EventType.EMAIL,
EventSubtype.EMAIL_INCORRECT_SAMPLE_TYPE
)
missing_sample_info = (
"Missing Sample Info",
"email/missing_sample_info.jinja2",
"email/missing_sample_info.plain",
("contact_name", "sample_barcode", "received_type", "resolution_url"),
EventType.EMAIL,
EventSubtype.EMAIL_MISSING_SAMPLE_INFO
)

def __init__(self, subject, html, plain, required, event_type, event_sub):
self.subject = subject
self.html = EmailTemplate(html, required)
self.plain = EmailTemplate(plain, required)
self.event_type = event_type
self.event_subtype = event_sub
41 changes: 41 additions & 0 deletions microsetta_private_api/api/microsetta_private_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,47 @@ paths:
application/json:
schema:
type: object
'/admin/events/accounts/{account_id}':
get:
operationId: microsetta_private_api.admin.admin_impl.get_account_events
tags:
- Admin
parameters:
- $ref: '#/components/parameters/account_id'
responses:
'200':
description: Event log associated with the account
content:
application/json:
schema:
type: array
'/admin/email':
post:
operationId: microsetta_private_api.admin.admin_impl.send_email
tags:
- Admin
summary: Send a templated email to an end user
description: Send a templated email to an end user
requestBody:
content:
application/json:
schema:
type: object
properties:
# issue type defines what resolution_url the user should go to
issue_type:
type: string
enum: ["sample"]
template:
type: string
enum: ["incorrect_sample_type",
"missing_sample_info"]
example: "incorrect_sample_type"
template_args:
type: object
responses:
'204':
description: Email sent

components:
parameters:
Expand Down
5 changes: 3 additions & 2 deletions microsetta_private_api/api/tests/test_admin_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
from microsetta_private_api.repo.transaction import Transaction
from microsetta_private_api.repo.account_repo import AccountRepo
from microsetta_private_api.repo.admin_repo import AdminRepo
from microsetta_private_api.api.tests.test_api import client, MOCK_HEADERS, \
ACCT_ID_1, ACCT_MOCK_ISS, ACCT_MOCK_SUB # noqa
from microsetta_private_api.api.tests.test_api import client # noqa Screw you flake8, we use this by name.
from microsetta_private_api.api.tests.test_api import MOCK_HEADERS, \
ACCT_ID_1, ACCT_MOCK_ISS, ACCT_MOCK_SUB

DUMMY_PROJ_NAME = "test project"

Expand Down
7 changes: 4 additions & 3 deletions microsetta_private_api/model/log_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ class EventSubtype(Enum):
EMAIL_SAMPLE_RECEIVED_PLATED = "sample_received_plated"
# indicate a previously banked sample is now being plated
EMAIL_BANKED_SAMPLE_NOW_PLATED = "banked_sample_plated"
# indicate if there is a problem with a sample
# (messaging should be tailored to the problem)
EMAIL_SAMPLE_RECEIVED_WITH_PROBLEMS = "sample_received_with_problems"
# Indicate a sample was received but doesn't match the information provided
EMAIL_INCORRECT_SAMPLE_TYPE = "incorrect_sample_type"
# Indicate a sample was received but necessary information was not provided
EMAIL_MISSING_SAMPLE_INFO = "missing_sample_info"


class LogEvent(ModelBase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<style type="text/css">ol{margin:0;padding:0}table td,table th{padding:0}.c2{background-color:#ffffff;margin-left:72pt;padding-top:0pt;padding-bottom:0pt;line-height:1.15;orphans:2;widows:2;text-align:left;height:11pt}.c1{background-color:#ffffff;margin-left:36pt;padding-top:0pt;padding-bottom:0pt;line-height:1.15;orphans:2;widows:2;text-align:left}.c3{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c0{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:12pt;font-family:"Arial";font-style:normal}.c6{color:#000000;font-weight:700;text-decoration:none;vertical-align:baseline;font-family:"Arial";font-style:normal}.c8{padding-top:0pt;padding-bottom:0pt;line-height:1.15;orphans:2;widows:2;text-align:left}.c10{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.c9{background-color:#ffffff;margin-left:72pt}.c7{font-weight:700}.c5{height:11pt}.c11{margin-left:36pt}.c4{font-size:12pt}.title{padding-top:0pt;color:#000000;font-size:26pt;padding-bottom:3pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:0pt;color:#666666;font-size:15pt;padding-bottom:16pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}li{color:#000000;font-size:11pt;font-family:"Arial"}p{margin:0;color:#000000;font-size:11pt;font-family:"Arial"}h1{padding-top:20pt;color:#000000;font-size:20pt;padding-bottom:6pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:18pt;color:#000000;font-size:16pt;padding-bottom:6pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:16pt;color:#434343;font-size:14pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:14pt;color:#666666;font-size:12pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h5{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h6{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;font-style:italic;orphans:2;widows:2;text-align:left}</style>
</head>
<body class="c10">

<p class="c1">
<span class="c0">Hello {{contact_name |e}},</span>
</p>
<p class="c1"><span class="c0">&nbsp;</span></p>
<p class="c8 c9">
<span class="c0">We recently received the following sample from you and noticed that the sample type may have been incorrectly assigned. To update the sample type, please click on the link below.</span>
</p>
<p class="c2"><span class="c0"></span></p>
<p class="c8 c9">
<ul class="c8 c9">
<li><span class="c0"> <a href={{resolution_url}} target="_blank">{{sample_barcode |e}}</a> - Marked {{recorded_type |e}} but appears to be {{received_type |e}}</span></li>
</ul>
</p>
<p class="c2">
<span class="c0"></span>
</p>
<p class="c8 c9">
<span class="c0">The sample type determines how the sample is handled in the lab, and ultimately how the sample results are assessed. We cannot process the sample until the sample type is verified.</span>
</p>
<p class="c8 c9"><span class="c0">&nbsp;</span></p>
<p class="c8 c9">
<span class="c4">If you believe {{received |e}} is correct, then please confirm with us at <a href = "mailto: microsetta@ucsd.edu">microsetta@ucsd.edu</a> by replying to this email. We will then update your information so the sample can be appropriately associated.</span>
</p>
<p class="c1 c5"><span class="c0"></span></p>
<p class="c1">
<span class="c0">If you have any questions, please reply to us at <a href = "mailto: microsetta@ucsd.edu">microsetta@ucsd.edu</a>. Thank you for supporting our project.</span>
</p>
<p class="c1"><span class="c0">&nbsp;</span></p>
<p class="c1">
<span class="c0">Sincerely,</span>
</p>
<p class="c8 c11">
<span class="c4">The Microsetta Team</span>
</p>
</body>
</html>
13 changes: 13 additions & 0 deletions microsetta_private_api/templates/email/incorrect_sample_type.plain
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Hello {{contact_name |e}},
We recently received the following sample from you and noticed that the sample type may have been incorrectly assigned.

Barcode {{sample_barcode |e}} - Marked {{recorded_type |e}} but appears to be {{received_type |e}}

The sample type determines how the sample is handled in the lab, and ultimately how the sample results are assessed. We cannot process the sample until the sample type is verified. If you believe {{received |e}} is correct, then please confirm with us at microsetta@ucsd.edu by replying to this email. We will then update your information so the sample can be appropriately associated.

If you have any questions, please reply to us at microsetta@ucsd.edu.

Thank you for supporting our project.

Sincerely,
The Microsetta Team
53 changes: 53 additions & 0 deletions microsetta_private_api/templates/email/missing_sample_info.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<style type="text/css">ol{margin:0;padding:0}table td,table th{padding:0}.c2{background-color:#ffffff;margin-left:72pt;padding-top:0pt;padding-bottom:0pt;line-height:1.15;orphans:2;widows:2;text-align:left;height:11pt}.c1{background-color:#ffffff;margin-left:36pt;padding-top:0pt;padding-bottom:0pt;line-height:1.15;orphans:2;widows:2;text-align:left}.c3{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c0{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:12pt;font-family:"Arial";font-style:normal}.c6{color:#000000;font-weight:700;text-decoration:none;vertical-align:baseline;font-family:"Arial";font-style:normal}.c8{padding-top:0pt;padding-bottom:0pt;line-height:1.15;orphans:2;widows:2;text-align:left}.c10{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.c9{background-color:#ffffff;margin-left:72pt}.c7{font-weight:700}.c5{height:11pt}.c11{margin-left:36pt}.c4{font-size:12pt}.title{padding-top:0pt;color:#000000;font-size:26pt;padding-bottom:3pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:0pt;color:#666666;font-size:15pt;padding-bottom:16pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}li{color:#000000;font-size:11pt;font-family:"Arial"}p{margin:0;color:#000000;font-size:11pt;font-family:"Arial"}h1{padding-top:20pt;color:#000000;font-size:20pt;padding-bottom:6pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:18pt;color:#000000;font-size:16pt;padding-bottom:6pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:16pt;color:#434343;font-size:14pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:14pt;color:#666666;font-size:12pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h5{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;orphans:2;widows:2;text-align:left}h6{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.15;page-break-after:avoid;font-style:italic;orphans:2;widows:2;text-align:left}</style>
</head>
<body class="c10">
<p class="c1 c5"><span class="c0"></span></p>
<p class="c1">
<span class="c0">Hello {{contact_name |e}},</span>
</p>
<p class="c1"><span class="c0">&nbsp;</span></p>
<p class="c8 c9">
<span class="c4">Thank you for your interest and participation in The Microsetta Initiative. We are writing as we've encountered an issue processing your sample, and we would appreciate your assistance in helping to resolve this matter. Specifically, the following sample sent to the lab does not have </span>
<span class="c4">a recorded sample type, date or time. Please click on the URL below to update the sample information.</span>
</p>
<p class="c2"><span class="c0"></span></p>
<p class="c8 c9">
<ul class="c8 c9">
<li><span class="c0"><a href={{resolution_url}} target="_blank">{{sample_barcode |e}}</a></span></li>
</ul>
</p>
<p class="c2">
<span class="c0"></span>
</p>
<p class="c8 c9">
<span class="c0">We require this information in order to process this sample in compliance with our human subjects research protocol. If you have a record of the associated sample type, date, and time for this sample, please provide us with this information. Based on what we can see, the sample type appears to be:</span>
</p>
<p class="c8 c9">
<span class="c0">&nbsp;</span>
</p>
<p class="c8 c9">
<ul class="c8 c9">
<li><span class="c0"><a href={{resolution_url}} target="_blank">{{sample_barcode |e}}</a> &ndash; {{received_type |e}}</span></li>
</ul>
</p>
<p class="c8 c9">
<span class="c0">&nbsp;</span>
</p>
<p class="c8 c9">
<span class="c0">If you believe {{received_type |e}} is correct, then please confirm with us at <a href = "mailto: microsetta@ucsd.edu">microsetta@ucsd.edu</a> by replying to this email. And if you have an approximate date and time of sample collection, we would greatly appreciate your sharing this information. We will then update your sample's information so it can be appropriately associated. Alternatively, if you would like to update the sample information directly, please click the link above to do so.</span>
</p>
<p class="c1 c5">
<span class="c0"></span>
</p>
<p class="c1">
<span class="c0">If you have any questions, please reply to us at <a href = "mailto: microsetta@ucsd.edu">microsetta@ucsd.edu</a></span>
</p>
<p class="c1"><span class="c0">&nbsp;</span></p>
<p class="c1"><span class="c0">Thank you,</span></p>
<p class="c8 c11"><span class="c0">The Microsetta Team</span></p>

</body>
</html>
17 changes: 17 additions & 0 deletions microsetta_private_api/templates/email/missing_sample_info.plain
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Hello {{contact_name |e}},
Thank you for your interest and participation in The Microsetta Initiative. We are writing as we've encountered an issue processing your sample, and we would appreciate your assistance in helping to resolve this matter.

Specifically, the following sample sent to the lab does not have a recorded sample type, date or time:
Barcode {{sample_barcode |e}}

We require this information in order to process this sample in compliance with our human subjects research protocol. If you have a record of the associated sample type, date, and time for this sample, please provide us with this information.

Based on what we can see, the sample type appears to be:
Barcode {{sample_barcode |e}}: {{received_type |e}}

If you believe {{received_type |e}} is correct, then please confirm with us at microsetta@ucsd.edu by replying to this email. And if you have an approximate date and time of sample collection, we would greatly appreciate your sharing this information. We will then update your sample's information so it can be appropriately associated.

If you have any questions, please reply to us at microsetta@ucsd.edu

Thank you,
The Microsetta Team

0 comments on commit 51a91de

Please sign in to comment.