Merge pull request #97 from danieldupriest/implement-sending-email
Implement sending email using Joe's code
This commit is contained in:
commit
b58fff8888
9 changed files with 119 additions and 15 deletions
4
back/.env
Normal file
4
back/.env
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
EMAIL_HOST_USER=accountemail@yourmail.com
|
||||||
|
EMAIL_HOST_PASSWORD=accountpasswordhere
|
||||||
|
SUBMIT_REPORT_DESTINATION_EMAIL=to-address@yourmail.com
|
||||||
|
SUBMIT_REPORT_FROM_EMAIL=from-address@yourmail.com
|
|
@ -12,6 +12,7 @@ djangorestframework = "==3.8.2"
|
||||||
django-rest-auth = "==0.9.3"
|
django-rest-auth = "==0.9.3"
|
||||||
django-allauth = "==0.37.1"
|
django-allauth = "==0.37.1"
|
||||||
gunicorn = "==19.6.0"
|
gunicorn = "==19.6.0"
|
||||||
|
python-decouple = "==3.1"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.5"
|
python_version = "3.5"
|
||||||
|
|
15
back/Pipfile.lock
generated
15
back/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "b1fc6b06ec8daa4efd9573865bc6c1732ae9354309e036bfe3ce0ab76b1a3bcd"
|
"sha256": "b8697c2d821e12f9f3e7bbad8a765782fc8a89df3f4260bc5f2b6f2e3d454510"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -39,11 +39,11 @@
|
||||||
},
|
},
|
||||||
"django": {
|
"django": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:a32c22af23634e1d11425574dce756098e015a165be02e4690179889b207c7a8",
|
"sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade",
|
||||||
"sha256:d6393918da830530a9516bbbcbf7f1214c3d733738779f06b0f649f49cc698c3"
|
"sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.1.5"
|
"version": "==2.1.7"
|
||||||
},
|
},
|
||||||
"django-allauth": {
|
"django-allauth": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -97,6 +97,13 @@
|
||||||
],
|
],
|
||||||
"version": "==3.0.1"
|
"version": "==3.0.1"
|
||||||
},
|
},
|
||||||
|
"python-decouple": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1317df14b43efee4337a4aa02914bf004f010cd56d6c4bd894e6474ec8c4fe2d"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.1"
|
||||||
|
},
|
||||||
"python3-openid": {
|
"python3-openid": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa",
|
"sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa",
|
||||||
|
|
|
@ -114,8 +114,8 @@ def fare_limit_rule(report, fields):
|
||||||
planning_section.add_rule(title="Fare limits", rule=fare_limit_rule)
|
planning_section.add_rule(title="Fare limits", rule=fare_limit_rule)
|
||||||
|
|
||||||
def lowest_fare_rule(report, fields):
|
def lowest_fare_rule(report, fields):
|
||||||
diff = field['lowest_fare_duration'] - field['preferred_flight_duration']
|
diff = fields['lowest_fare_duration'] - fields['preferred_flight_duration']
|
||||||
lowest_Fare = field['lowest_fare']
|
lowest_fare = fields['lowest_fare']
|
||||||
maximum = 0
|
maximum = 0
|
||||||
if diff <= 0:
|
if diff <= 0:
|
||||||
maximum = lowest_fare + 100
|
maximum = lowest_fare + 100
|
||||||
|
@ -127,14 +127,14 @@ def lowest_fare_rule(report, fields):
|
||||||
maximum = lowest_fare + 350
|
maximum = lowest_fare + 350
|
||||||
else:
|
else:
|
||||||
maximum = lowest_fare + 600
|
maximum = lowest_fare + 600
|
||||||
if field['preferred_fare'] > maximum:
|
if fields['preferred_fare'] > maximum:
|
||||||
return "For the lowest fare you have provided, your maximum in-policy fare amount is {} USD.".format(maximum)
|
return "For the lowest fare you have provided, your maximum in-policy fare amount is {} USD.".format(maximum)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
planning_section.add_rule(title="Lowest fare check", rule=lowest_fare_rule)
|
planning_section.add_rule(title="Lowest fare check", rule=lowest_fare_rule)
|
||||||
|
|
||||||
def departure_date_limit_rule(report, fields):
|
def departure_date_limit_rule(report, fields):
|
||||||
days_to_departure = date(field['departure_date']) - date(field['screenshot_date'])
|
days_to_departure = date(fields['departure_date']) - date(fields['screenshot_date'])
|
||||||
if days_to_departure < 14:
|
if days_to_departure < 14:
|
||||||
return "Flights must be booked at least 14 days in advance."
|
return "Flights must be booked at least 14 days in advance."
|
||||||
if days_to_departure > 365:
|
if days_to_departure > 365:
|
||||||
|
@ -174,7 +174,7 @@ flight_section.add_rule(title="Fare limits", rule=actual_fare_limit_rule)
|
||||||
|
|
||||||
def request_date_rule(report, fields):
|
def request_date_rule(report, fields):
|
||||||
now = date.today()
|
now = date.today()
|
||||||
last_travel = date(field['return_date'])
|
last_travel = date(fields['return_date'])
|
||||||
if now - last_travel > 90:
|
if now - last_travel > 90:
|
||||||
return "Reimbursement requests must be made within 90 days of the last day of travel."
|
return "Reimbursement requests must be made within 90 days of the last day of travel."
|
||||||
return None
|
return None
|
||||||
|
|
26
back/backend/templates/backend/email.html
Normal file
26
back/backend/templates/backend/email.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Title: {{ title }}</h1>
|
||||||
|
{% for section in sections %}
|
||||||
|
{% if section.completed %}
|
||||||
|
<h2>{{section.title}}</h2>
|
||||||
|
<table border="1">
|
||||||
|
{% for field in section.fields %}
|
||||||
|
<tr>
|
||||||
|
<td>{{field.label}}</td>
|
||||||
|
<td>{{field.value|default:""}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% for rule in section.rule_violations %}
|
||||||
|
<tr style="color:#ff3333">
|
||||||
|
<td colspan=2><strong>{{rule.label}}</strong><br/>{{rule.rule_break_text}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</html>
|
15
back/backend/templates/backend/email.txt
Normal file
15
back/backend/templates/backend/email.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
***Deprecated:
|
||||||
|
Replaced by converting html template to plaintext
|
||||||
|
using the html2text library
|
||||||
|
***
|
||||||
|
|
||||||
|
Title: {{title}}
|
||||||
|
{% for section in sections %}
|
||||||
|
{{section.title}}
|
||||||
|
{% for field in section.fields %}
|
||||||
|
{{field.label}}: {{field.value|default:"empty"}}
|
||||||
|
{% endfor %}
|
||||||
|
{% for rule in section.rule_violations %}
|
||||||
|
[RULE] {{rule.label}}: {{rule.rule_break_text}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
|
@ -3,6 +3,11 @@ from django.http import JsonResponse
|
||||||
from .models import *
|
from .models import *
|
||||||
from .policy import pol
|
from .policy import pol
|
||||||
import os
|
import os
|
||||||
|
from rest_framework.exceptions import ParseError
|
||||||
|
from rest_framework.parsers import FileUploadParser, MultiPartParser
|
||||||
|
from django.core.mail import EmailMultiAlternatives
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from decouple import config
|
||||||
|
|
||||||
def get_report(report_pk):
|
def get_report(report_pk):
|
||||||
"""
|
"""
|
||||||
|
@ -199,8 +204,8 @@ def report_detail(request, report_pk):
|
||||||
return JsonResponse({"message": "Cannot submit a report that has already been submitted."}, status=409)
|
return JsonResponse({"message": "Cannot submit a report that has already been submitted."}, status=409)
|
||||||
rep.submitted = True;
|
rep.submitted = True;
|
||||||
rep.save()
|
rep.save()
|
||||||
# Send email here
|
# Send email
|
||||||
#################
|
send_report_to_admin(request, report_pk)
|
||||||
return JsonResponse({"message": "Report submitted."})
|
return JsonResponse({"message": "Report submitted."})
|
||||||
|
|
||||||
# DELETE: Deletes a report from the user's account.
|
# DELETE: Deletes a report from the user's account.
|
||||||
|
@ -365,3 +370,46 @@ def section_complete(section_pk):
|
||||||
if i.completed:
|
if i.completed:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def send_report_to_admin(request, report_pk):
|
||||||
|
"""
|
||||||
|
Sends an email message to admin with html/txt version of report,
|
||||||
|
along with file attachments. Cc sent to user.
|
||||||
|
|
||||||
|
request -- Request object with user info inside
|
||||||
|
report_pk -- ID of the report to submit
|
||||||
|
"""
|
||||||
|
params = get_report(report_pk)
|
||||||
|
to_email = config('SUBMIT_REPORT_DESTINATION_EMAIL')
|
||||||
|
from_email = config('SUBMIT_REPORT_FROM_EMAIL')
|
||||||
|
cc = request.user.email
|
||||||
|
msg_html = render_to_string('backend/email.html', params)
|
||||||
|
msg_plain = render_to_string('backend/email.txt', params)
|
||||||
|
message = EmailMultiAlternatives(
|
||||||
|
"Reimbursinator - {}".format(params['title']),
|
||||||
|
msg_plain,
|
||||||
|
from_email,
|
||||||
|
[to_email],
|
||||||
|
cc=[request.user.email],
|
||||||
|
)
|
||||||
|
message.attach_alternative(msg_html, "text/html")
|
||||||
|
for f in get_files(report_pk):
|
||||||
|
message.attach_file(f)
|
||||||
|
message.send()
|
||||||
|
|
||||||
|
def get_files(report_pk):
|
||||||
|
"""
|
||||||
|
collects all the files in a particular report and returns them
|
||||||
|
in an array.
|
||||||
|
|
||||||
|
report_pk -- ID of the report to collect files for
|
||||||
|
"""
|
||||||
|
sections = Section.objects.filter(report_id=report_pk)
|
||||||
|
files = []
|
||||||
|
for section in sections:
|
||||||
|
fields = Field.objects.filter(section_id=section.id, completed=True)
|
||||||
|
for field in fields:
|
||||||
|
if field.field_type == "file":
|
||||||
|
print("appending {}".format(field.data_file.name))
|
||||||
|
files.append(field.data_file.name)
|
||||||
|
return files
|
||||||
|
|
BIN
back/db.sqlite3
BIN
back/db.sqlite3
Binary file not shown.
|
@ -11,12 +11,11 @@ https://docs.djangoproject.com/en/2.1/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
#from reimbursinator.custom_auth import BearerAuthentication
|
from decouple import config
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
|
||||||
|
|
||||||
|
@ -28,7 +27,6 @@ DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['localhost','192.168.99.100', '127.0.0.1']
|
ALLOWED_HOSTS = ['localhost','192.168.99.100', '127.0.0.1']
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
@ -157,7 +155,12 @@ STATIC_URL = 'https://192.168.99.100:8445/'
|
||||||
|
|
||||||
# Email Config
|
# Email Config
|
||||||
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||||
|
EMAIL_USE_TLS = True
|
||||||
|
EMAIL_HOST = 'smtp.gmail.com'
|
||||||
|
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
|
||||||
|
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
|
||||||
|
EMAIL_PORT = 587
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue