From ba9b5d02b240744cdb307b3eedb3185f2d42bbd6 Mon Sep 17 00:00:00 2001 From: Leo Kivikunnas Date: Mon, 9 Sep 2019 20:29:00 +0300 Subject: [PATCH] Add attachment to emails --- expenses_claim/forms.py | 23 +++--- expenses_claim/templates/claim.html | 8 +- expenses_claim/templates/claim2pdf.html | 20 +++++ expenses_claim/views.py | 27 +++++-- members/views/utils.py | 2 + requirements.txt | 100 ++++++++++++++++-------- webapp/utils.py | 17 +++- 7 files changed, 142 insertions(+), 55 deletions(-) create mode 100644 expenses_claim/templates/claim2pdf.html diff --git a/expenses_claim/forms.py b/expenses_claim/forms.py index 0c40442..e616ab6 100644 --- a/expenses_claim/forms.py +++ b/expenses_claim/forms.py @@ -9,22 +9,23 @@ class ExpensesClaim(forms.Form): name = forms.CharField(label='Nimi', max_length=100) iban = forms.CharField(label='IBAN', max_length=100) - amount = forms.DecimalField(label="Summa", decimal_places=2) + amount = forms.DecimalField(label='Summa', decimal_places=2) def clean_iban(self): """Validate IBAN.""" - data = self.cleaned_data['iban'] + orig_iban = self.cleaned_data['iban'] # Remove spaces. - data = data.replace(" ", "") + cleaned_iban = orig_iban.replace(' ', '') # Move first 4 symbols to the end of the string. - data = data[4:] + data[0:4] + cleaned_iban = cleaned_iban[4:] + cleaned_iban[0:4] LETTERS = {letter: str(index) for index, letter in enumerate(ascii_uppercase, start=10)} - data = data.upper() + cleaned_iban = cleaned_iban.upper() # Replace all letters with numbers, so that A=10, B=11, ..., Z=35. - data = [LETTERS[char] if char in LETTERS else char for char in data] - data = ''.join(data) - # If data modulo 97 != 1 the IBAN number is invalid. - if int(data) % 97 != 1: - raise forms.ValidationError("Invalid IBAN number!") - return data + cleaned_iban = [LETTERS[char] if char in LETTERS + else char for char in cleaned_iban] + cleaned_iban = ''.join(cleaned_iban) + # If cleaned_iban modulo 97 != 1 the IBAN number is invalid. + if int(cleaned_iban) % 97 != 1: + raise forms.ValidationError('Invalid IBAN number!') + return orig_iban diff --git a/expenses_claim/templates/claim.html b/expenses_claim/templates/claim.html index 2afd7bb..63bc724 100644 --- a/expenses_claim/templates/claim.html +++ b/expenses_claim/templates/claim.html @@ -1,8 +1,8 @@ Dis is claim
- {{ form }} - - - {% csrf_token %} + {{ form }} + + + {% csrf_token %}
diff --git a/expenses_claim/templates/claim2pdf.html b/expenses_claim/templates/claim2pdf.html new file mode 100644 index 0000000..ae002b6 --- /dev/null +++ b/expenses_claim/templates/claim2pdf.html @@ -0,0 +1,20 @@ + + + + Raha-anomus + + +

Raha-anomus

+
+
+ {{ name }} +
+
+ {{ iban }} +
+
+ {{ amount }} +
+
+ + diff --git a/expenses_claim/views.py b/expenses_claim/views.py index d963dc9..cb34841 100644 --- a/expenses_claim/views.py +++ b/expenses_claim/views.py @@ -3,11 +3,13 @@ from django.shortcuts import render from django.views.decorators.http import require_http_methods from django.http import HttpResponse -from webapp.utils import send_email +from webapp.utils import send_email_with_attachment +from django.template.loader import render_to_string +from weasyprint import HTML +import tempfile from .forms import ExpensesClaim -# Allow only GET or POST @require_http_methods(["GET", "POST"]) def claim(request): """Render expenses claim form.""" @@ -15,11 +17,24 @@ def claim(request): if request.method == 'POST': form = ExpensesClaim(request.POST) if form.is_valid(): - email = "leo.kivikunnas@gmail.com" + name = form.cleaned_data['name'] + amount = form.cleaned_data['amount'] + iban = form.cleaned_data['iban'] + html_string = render_to_string('claim2pdf.html', + {'name': name, 'iban': iban, + 'amount': amount}).encode('UTF-8') + html = HTML(string=html_string) + attachment = html.write_pdf() + response = HttpResponse( + attachment, content_type='application/pdf;' + ) + response['Content-Disposition'] = 'filename=claim.pdf' + + email = "leo.kivikunnas@aalto.fi" subject = "Test expenses claim" - message = "Test" - send_email(email, subject, message) - return HttpResponse() + body = "Test" + send_email_with_attachment(email, subject, body, attachment) + return response elif request.method == 'GET': form = ExpensesClaim() diff --git a/members/views/utils.py b/members/views/utils.py index d6c1f8f..b908e31 100644 --- a/members/views/utils.py +++ b/members/views/utils.py @@ -145,6 +145,8 @@ def import_csv(request, *args, **kwargs): return render(request, 'member_add_many_confirm.html', context) +# TODO: There is also a similar wrapper in common utils why +# not just use that? def send_mail_wrapper(subject, message, email_to): """Send mail to default email.""" send_mail(subject, diff --git a/requirements.txt b/requirements.txt index a5764be..6616a93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,39 +1,75 @@ +aiohttp==3.6.0 +async-timeout==3.0.1 +attrs==19.1.0 +Babel==2.7.0 +backports.csv==1.0.7 backports.shutil-get-terminal-size==1.0.0 +cairocffi==1.1.0 +CairoSVG==2.4.1 +cffi==1.12.3 +chardet==3.0.4 +coverage==4.3.4 +cssselect2==0.2.2 +dealer==2.0.5 decorator==4.0.9 +defusedxml==0.6.0 +diff-match-patch==20181111 Django==2.1.5 +django-app-namespace-template-loader==0.4.1 +django-auditlog==0.4.5 +django-autocomplete-light==3.2.10 +django-bootstrap3==8.2.3 +django-cors-headers==2.0.1 +django-filter==2.0.0 +django-import-export==0.7.0 +django-jsonfield==1.3.1 +django-modeltranslation==0.13b1 +django-nocaptcha-recaptcha==0.0.19 +django-nose==1.4.5 +django-phonenumber-field==1.3.0 +django-suit==0.2.26 +django-tables2==1.6.1 +djangorestframework==3.8.2 +djangorestframework-jwt==1.11.0 +et-xmlfile==1.0.1 +html5lib==1.0.1 +idna==2.8 ipython==4.2.0 ipython-genutils==0.1.0 -pexpect==4.1.0 -pickleshare==0.7.2 -ptyprocess==0.5.1 -pytz==2016.4 -simplegeneric==0.8.1 -traitlets==4.2.1 -Pillow==5.4.1 -requests==2.11.1 -django-nocaptcha-recaptcha==0.0.19 -django-cors-headers==2.0.1 -djangorestframework==3.8.2 -PyJWT==1.6.4 -djangorestframework-jwt==1.11.0 -coverage==4.3.4 -django-nose==1.4.5 +jdcal==1.4.1 +lml==0.0.9 +multidict==4.5.2 +nose==1.3.7 nose-exclude==0.5.0 -psycopg2-binary==2.7.6.1 -django-bootstrap3==8.2.3 -django-tables2==1.6.1 -pycodestyle==2.3.1 -dealer==2.0.5 -django-modeltranslation==0.13b1 -django-auditlog==0.4.5 -django-phonenumber-field==1.3.0 -django-autocomplete-light==3.2.10 -six==1.10.0 -django-suit==0.2.26 -telepot==12.3 -pyexcel==0.5.10 -pyexcel-xlsx==0.5.5 -django-import-export==0.7.0 +odfpy==1.4.0 openpyxl==2.4.11 -django-app-namespace-template-loader==0.4.1 -django-filter==2.0.0 +pexpect==4.1.0 +phonenumberslite==8.10.18 +pickleshare==0.7.2 +Pillow==5.4.1 +psycopg2-binary==2.7.6.1 +ptyprocess==0.5.1 +pycodestyle==2.3.1 +pycparser==2.19 +pyexcel==0.5.10 +pyexcel-io==0.5.20 +pyexcel-xlsx==0.5.5 +PyJWT==1.6.4 +Pyphen==0.9.5 +python-dateutil==2.6.0 +pytz==2016.4 +PyYAML==5.1.2 +requests==2.11.1 +simplegeneric==0.8.1 +six==1.10.0 +tablib==0.13.0 +telepot==12.3 +texttable==1.6.2 +tinycss2==1.0.2 +traitlets==4.2.1 +urllib3==1.25.3 +WeasyPrint==48 +webencodings==0.5.1 +xlrd==1.2.0 +xlwt==1.3.0 +yarl==1.3.0 diff --git a/webapp/utils.py b/webapp/utils.py index 5e0cdd2..2aa850f 100644 --- a/webapp/utils.py +++ b/webapp/utils.py @@ -1,8 +1,7 @@ """Webapp utils.""" from django.utils import timezone -from django.core.mail import send_mail - +from django.core.mail import send_mail, EmailMessage from datetime import timedelta import logging from django.conf import settings @@ -27,3 +26,17 @@ def send_email(to, subject, body): except Exception as ex: logging.exception('Failed to send email.') + + +def send_email_with_attachment(to, subject, body, attachment): + try: + email = EmailMessage( + subject, body, settings.DEFAULT_EMAIL_FROM, [to] + ) + email.attach('raha.pdf', attachment, 'application/pdf;') + res = email.send() + if res == 0: + raise Exception('Failed to send email!') + + except Exception as ex: + logging.exception('Failed to send email.')