Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06723e3f69 | |||
| ba9b5d02b2 | |||
| 242d143a5e |
+14
-12
@@ -5,26 +5,28 @@ from string import ascii_uppercase
|
|||||||
|
|
||||||
|
|
||||||
class ExpensesClaim(forms.Form):
|
class ExpensesClaim(forms.Form):
|
||||||
"""Expenses claim form"""
|
"""Expenses claim form."""
|
||||||
|
|
||||||
name = forms.CharField(label='Nimi', max_length=100)
|
name = forms.CharField(label='Nimi', max_length=100)
|
||||||
iban = forms.CharField(label='IBAN', max_length=100)
|
iban = forms.CharField(label='IBAN', max_length=100)
|
||||||
amount = forms.DecimalField(label="Summa", decimal_places=2)
|
email = forms.EmailField(label='Sähköposti', max_length=100)
|
||||||
|
amount = forms.DecimalField(label='Yhteensä', decimal_places=2)
|
||||||
|
|
||||||
def clean_iban(self):
|
def clean_iban(self):
|
||||||
"""Validate IBAN."""
|
"""Validate IBAN."""
|
||||||
data = self.cleaned_data['iban']
|
orig_iban = self.cleaned_data['iban']
|
||||||
# Remove spaces.
|
# Remove spaces.
|
||||||
data = data.replace(" ", "")
|
cleaned_iban = orig_iban.replace(' ', '')
|
||||||
# Move first 4 symbols to the end of the string.
|
# 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,
|
LETTERS = {letter: str(index) for index,
|
||||||
letter in enumerate(ascii_uppercase, start=10)}
|
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.
|
# 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]
|
cleaned_iban = [LETTERS[char] if char in LETTERS
|
||||||
data = ''.join(data)
|
else char for char in cleaned_iban]
|
||||||
# If data modulo 97 != 1 the IBAN number is invalid.
|
cleaned_iban = ''.join(cleaned_iban)
|
||||||
if int(data) % 97 != 1:
|
# If cleaned_iban modulo 97 != 1 the IBAN number is invalid.
|
||||||
raise forms.ValidationError("Invalid IBAN number!")
|
if int(cleaned_iban) % 97 != 1:
|
||||||
return data
|
raise forms.ValidationError('Invalid IBAN number!')
|
||||||
|
return orig_iban
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
padding: 20px 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.claim-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 20px;
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: space-between;
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollarea {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tablerow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 10px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldscolumn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldsrow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit {
|
||||||
|
align-self: center;
|
||||||
|
width: 30%;
|
||||||
|
margin: 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageupload > input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldsrow > div {
|
||||||
|
margin: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldscolumn > div {
|
||||||
|
margin: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.fieldscolumn > div > label {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.money > input {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#addreceipt {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#total {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#receiptslist {
|
||||||
|
height: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#info2 {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#textbox > textarea {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#id_iban {
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.fieldscolumn > #textbox > textarea {
|
||||||
|
height: 8rem;
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
.heading {
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.heading {
|
||||||
|
visibility: hidden;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
padding: 5px 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
#total {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldsrow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 5px 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
#receiptslist {
|
||||||
|
width: 12rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-height: 741px) {
|
||||||
|
#receiptslist {
|
||||||
|
height: 17rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-height: 569px) {
|
||||||
|
#receiptslist {
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
function addReceipt(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var receipts = document.getElementById("receiptslist");
|
||||||
|
var div2append = document.createElement("div");
|
||||||
|
var newrow = receipts.appendChild(div2append);
|
||||||
|
newrow.classList.add("tablerow");
|
||||||
|
|
||||||
|
div2append = document.createElement("div");
|
||||||
|
var div1 = newrow.appendChild(div2append);
|
||||||
|
div1.classList.add("imageupload");
|
||||||
|
div2append = document.createElement("div");
|
||||||
|
var div2 = newrow.appendChild(div2append);
|
||||||
|
div2.classList.add("desc");
|
||||||
|
div2append = document.createElement("div");
|
||||||
|
var div3 = newrow.appendChild(div2append);
|
||||||
|
div3.classList.add("money");
|
||||||
|
|
||||||
|
var file = document.getElementById("newreceipt");
|
||||||
|
var desc = document.getElementById("adddescription");
|
||||||
|
var sum = document.getElementById("addsum");
|
||||||
|
div1.appendChild(file.cloneNode(true));
|
||||||
|
div2.appendChild(document.createTextNode(desc.value));
|
||||||
|
div3.appendChild(document.createTextNode(sum.value));
|
||||||
|
|
||||||
|
var total = document.getElementById("totalsum");
|
||||||
|
total.value = Number(total.value) + Number(sum.value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "project.html" %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
<script src="{% static "claim_form/js/addReceipt.js" %}"></script>
|
||||||
|
<link rel="stylesheet" href="{% static "claim_form/css/base.css" %}"></script>
|
||||||
|
{% endblock styles %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="claim-form">
|
||||||
|
{% block content %}
|
||||||
|
{% include "expenses_claim:form.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
Dis is claim
|
|
||||||
|
|
||||||
<form action="" method="post">
|
|
||||||
{{ form }}
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
{% csrf_token %}
|
|
||||||
</form>
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Raha-anomus</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Raha-anomus</h1>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{{ name }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ iban }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ amount }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="form" id="claim" action="" method="post">
|
||||||
|
<h1 class="heading">Aalto Yliopiston Sähköinsinöörikilta ry Raha-anomuslomake</h1>
|
||||||
|
<div class="fieldsrow">
|
||||||
|
<div class="imageupload" id="newreceipt">
|
||||||
|
<label for="newreceipt">
|
||||||
|
Kuitti
|
||||||
|
<img class="thumbnail" src="{% static "claim_form/img/icon.png" %}"/>
|
||||||
|
</label>
|
||||||
|
<input id="selectreceipt" type="file"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="adddescription">
|
||||||
|
Kuvaus
|
||||||
|
</label>
|
||||||
|
<input id="adddescription" type="text">
|
||||||
|
</div>
|
||||||
|
<div class="money">
|
||||||
|
<label for="addsum">
|
||||||
|
Summa
|
||||||
|
</label>
|
||||||
|
<input type="number" id="addsum" maxlength=100/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="addreceipt" onclick="addReceipt(event)">Lisää kuitti</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<div class="scrollarea" id="receiptslist">
|
||||||
|
<div class="tablerow">
|
||||||
|
<div class="tableelement">Kuitti</div>
|
||||||
|
<div class="tableelement">Kuvaus</div>
|
||||||
|
<div class="tableelement">Summa</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="money" id="total">
|
||||||
|
<label for="id_amount">Yhteensä</label>
|
||||||
|
{{ form.amount }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fieldscolumn">
|
||||||
|
<div class="fieldsrow">
|
||||||
|
<div class="fieldscolumn" id="info1">
|
||||||
|
<div id="textbox">
|
||||||
|
<label for="selite">Selite</label>
|
||||||
|
<textarea id="selite"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="id_iban">{{ form.iban.label }}</label>
|
||||||
|
{{ form.iban }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fieldscolumn" id="info2">
|
||||||
|
<div>
|
||||||
|
<label for="id_name">{{ form.name.label }}</label>
|
||||||
|
{{ form.name }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="id_email">{{ form.email.label }}</label>
|
||||||
|
{{ form.email }}
|
||||||
|
</div>
|
||||||
|
<input class="submit" id="submitclaim" type="submit" value="Submit"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% csrf_token %}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
@@ -8,6 +8,7 @@ class ExpensesClaimTest(TestCase):
|
|||||||
def test_valid_data1(self):
|
def test_valid_data1(self):
|
||||||
form = ExpensesClaim({
|
form = ExpensesClaim({
|
||||||
'name': "John Doe",
|
'name': "John Doe",
|
||||||
|
'email': 'john@doe.com',
|
||||||
'iban': "FI37 1590 3000 0007 76",
|
'iban': "FI37 1590 3000 0007 76",
|
||||||
'amount': 12.54
|
'amount': 12.54
|
||||||
})
|
})
|
||||||
@@ -16,6 +17,7 @@ class ExpensesClaimTest(TestCase):
|
|||||||
def test_valid_data2(self):
|
def test_valid_data2(self):
|
||||||
form = ExpensesClaim({
|
form = ExpensesClaim({
|
||||||
'name': "John Cena",
|
'name': "John Cena",
|
||||||
|
'email': 'john@cena.com',
|
||||||
'iban': "AL35202111090000000001234567",
|
'iban': "AL35202111090000000001234567",
|
||||||
'amount': 12
|
'amount': 12
|
||||||
})
|
})
|
||||||
@@ -24,6 +26,7 @@ class ExpensesClaimTest(TestCase):
|
|||||||
def test_valid_data3(self):
|
def test_valid_data3(self):
|
||||||
form = ExpensesClaim({
|
form = ExpensesClaim({
|
||||||
'name': "John Wayne",
|
'name': "John Wayne",
|
||||||
|
'email': 'john@wayne.com',
|
||||||
'iban': "BR1500000000000010932840814P2",
|
'iban': "BR1500000000000010932840814P2",
|
||||||
'amount': 12.0
|
'amount': 12.0
|
||||||
})
|
})
|
||||||
@@ -32,6 +35,7 @@ class ExpensesClaimTest(TestCase):
|
|||||||
def test_invalid_iban(self):
|
def test_invalid_iban(self):
|
||||||
form = ExpensesClaim({
|
form = ExpensesClaim({
|
||||||
'name': "John Lennon",
|
'name': "John Lennon",
|
||||||
|
'email': 'john@lennon.com',
|
||||||
'iban': "FI3734 1590 3000 0007 76",
|
'iban': "FI3734 1590 3000 0007 76",
|
||||||
'amount': 12.54
|
'amount': 12.54
|
||||||
})
|
})
|
||||||
@@ -40,6 +44,7 @@ class ExpensesClaimTest(TestCase):
|
|||||||
def test_invalid_amount(self):
|
def test_invalid_amount(self):
|
||||||
form = ExpensesClaim({
|
form = ExpensesClaim({
|
||||||
'name': "John Kenedy",
|
'name': "John Kenedy",
|
||||||
|
'email': 'john@kenedy.com',
|
||||||
'iban': "FI37 1590 3000 0007 76",
|
'iban': "FI37 1590 3000 0007 76",
|
||||||
'amount': "asd"
|
'amount': "asd"
|
||||||
})
|
})
|
||||||
@@ -48,6 +53,7 @@ class ExpensesClaimTest(TestCase):
|
|||||||
def test_invalid_amount_decimal_places(self):
|
def test_invalid_amount_decimal_places(self):
|
||||||
form = ExpensesClaim({
|
form = ExpensesClaim({
|
||||||
'name': "John Travolta",
|
'name': "John Travolta",
|
||||||
|
'email': 'john@travolta.com',
|
||||||
'iban': "FI37 1590 3000 0007 76",
|
'iban': "FI37 1590 3000 0007 76",
|
||||||
'amount': 12.544
|
'amount': 12.544
|
||||||
})
|
})
|
||||||
|
|||||||
+22
-3
@@ -3,10 +3,12 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from webapp.utils import send_email_with_attachment
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from weasyprint import HTML
|
||||||
from .forms import ExpensesClaim
|
from .forms import ExpensesClaim
|
||||||
|
|
||||||
|
|
||||||
# Allow only GET or POST
|
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_http_methods(["GET", "POST"])
|
||||||
def claim(request):
|
def claim(request):
|
||||||
"""Render expenses claim form."""
|
"""Render expenses claim form."""
|
||||||
@@ -14,9 +16,26 @@ def claim(request):
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = ExpensesClaim(request.POST)
|
form = ExpensesClaim(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
return HttpResponse()
|
name = form.cleaned_data['name']
|
||||||
|
amount = form.cleaned_data['amount']
|
||||||
|
iban = form.cleaned_data['iban']
|
||||||
|
html_string = render_to_string('expenses_claim: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"
|
||||||
|
body = "Test"
|
||||||
|
send_email_with_attachment(email, subject, body, attachment)
|
||||||
|
return response
|
||||||
|
|
||||||
elif request.method == 'GET':
|
elif request.method == 'GET':
|
||||||
form = ExpensesClaim()
|
form = ExpensesClaim()
|
||||||
|
|
||||||
return render(request, 'claim.html', {'form': form})
|
return render(request, 'expenses_claim:base.html', {'form': form})
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ def import_csv(request, *args, **kwargs):
|
|||||||
return render(request, 'member_add_many_confirm.html', context)
|
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):
|
def send_mail_wrapper(subject, message, email_to):
|
||||||
"""Send mail to default email."""
|
"""Send mail to default email."""
|
||||||
send_mail(subject,
|
send_mail(subject,
|
||||||
|
|||||||
+68
-32
@@ -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
|
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
|
decorator==4.0.9
|
||||||
|
defusedxml==0.6.0
|
||||||
|
diff-match-patch==20181111
|
||||||
Django==2.1.5
|
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==4.2.0
|
||||||
ipython-genutils==0.1.0
|
ipython-genutils==0.1.0
|
||||||
pexpect==4.1.0
|
jdcal==1.4.1
|
||||||
pickleshare==0.7.2
|
lml==0.0.9
|
||||||
ptyprocess==0.5.1
|
multidict==4.5.2
|
||||||
pytz==2016.4
|
nose==1.3.7
|
||||||
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
|
|
||||||
nose-exclude==0.5.0
|
nose-exclude==0.5.0
|
||||||
psycopg2-binary==2.7.6.1
|
odfpy==1.4.0
|
||||||
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
|
|
||||||
openpyxl==2.4.11
|
openpyxl==2.4.11
|
||||||
django-app-namespace-template-loader==0.4.1
|
pexpect==4.1.0
|
||||||
django-filter==2.0.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
|
||||||
|
|||||||
+15
-2
@@ -1,8 +1,7 @@
|
|||||||
"""Webapp utils."""
|
"""Webapp utils."""
|
||||||
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail, EmailMessage
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -27,3 +26,17 @@ def send_email(to, subject, body):
|
|||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logging.exception('Failed to send email.')
|
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.')
|
||||||
|
|||||||
Reference in New Issue
Block a user