Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
Added email support to Tikker: sending an email with transaction over…
Browse files Browse the repository at this point in the history
…view, dinner overview and high debt if necessary.

Moved secret passwords and keys to environment variables
Updated flask to newest version
  • Loading branch information
Yoronex committed Dec 31, 2019
1 parent 22bab51 commit 751fbe0
Show file tree
Hide file tree
Showing 16 changed files with 381 additions and 19 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,5 @@ migrations_old_2/script.py.mako
migrations_old_2/versions/6b61e689098e_create_table_transaction.py
migrations_old_2/versions/9925adbe0c25_create_all_tables.py
/app.db
app/static/covers/
covers/
2 changes: 2 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from flask_migrate import Migrate
from flask_bootstrap import Bootstrap
from flask_breadcrumbs import Breadcrumbs
from flask_mail import Mail
from logging.handlers import RotatingFileHandler
import os
import zipfile
Expand Down Expand Up @@ -42,6 +43,7 @@
Bootstrap(app)
breadcrumbs = Breadcrumbs(app=app)
app.config['BOOTSTRAP_SERVE_LOCAL'] = True
mail = Mail(app)

from app import routes, models

Expand Down
21 changes: 20 additions & 1 deletion app/dbhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
settings = {}
borrel_mode_enabled = False
borrel_mode_drinks = []
overview_emails = False
debt_emails = False


def initialize_settings():
Expand All @@ -22,7 +24,9 @@ def initialize_settings():
'borrel_mode_user': '-1',
'borrel_mode_drinks': '[]',
'borrel_mode_amount': '0',
'borrel_mode_start_amount': '0'}
'borrel_mode_start_amount': '0',
'last_overview_email': '2019-07-01',
'last_debt_email': '2019-07-01'}
# Get all settings that are in the database
sses = Setting.query.all()
settings_keys = []
Expand All @@ -49,6 +53,21 @@ def initialize_settings():
initialize_settings()


# Determine whether an option should be given to send emails with monthly overviews
now = datetime.now()
last_overview_email = datetime.strptime(settings.get('last_overview_email'), "%Y-%m-%d")
last_debt_email = datetime.strptime(settings.get('last_debt_email'), "%Y-%m-%d")
if now > last_overview_email and now.month != last_overview_email.month and now.day > 3:
overview_emails = True
if now > last_debt_email and (now - last_debt_email).days >= 3:
debt_emails = True


@app.context_processor
def emails():
return dict(send_overview_emails=overview_emails, send_debt_emails=debt_emails)


def update_settings(key, value):
# Change the settings entry accordingly
settings[key] = value
Expand Down
150 changes: 150 additions & 0 deletions app/emailhandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import socket
from datetime import datetime, timedelta

from flask import render_template, flash
from flask_mail import Message
from sqlalchemy import and_

from app import app, mail, dbhandler, db
from app.models import User, Purchase, Transaction, Product, Upgrade

enabled = True
if app.config['MAIL_PASSWORD'] is '':
enabled = False
disable_reason = "Er is geen wachtwoord van de mailserver bekend"


def set_default_lastoverview():
for u in User.query.all():
if u.lastoverview is None:
u.lastoverview = datetime.strptime("2019-07-01", "%Y-%m-%d")
db.session.commit()


def test_debt_email():
emails = create_debt_emails([User.query.get(1)])
send_emails(emails)


def test_dinner_overview_email():
emails = create_overview_dinner_emails([User.query.get(1)])
send_emails(emails)


def test_overview_email():
emails = create_overview_emails([User.query.get(1)])
send_emails(emails)


def send_debt_emails():
users = User.query.all()
emails = create_debt_emails(users)
try:
send_emails(emails)
flash("Emails succesvol verstuurd!", "success")
dbhandler.update_settings('last_debt_email', datetime.now().strftime("%Y-%m-%d"))
except socket.gaierror:
flash("Kon geen verbinding maken met de KVLS Server. Weet je zeker dat de computer internet heeft?", "danger")


def send_overview_emails():
users = User.query.all()
begindate = datetime.strptime(dbhandler.settings['last_overview_email'], "%Y-%m-%d")
# enddate = datetime.now().replace(day=1)
enddate = datetime(year=2020, month=1, day=1)
if enddate.weekday() >= 4:
enddate += timedelta(days=7 - enddate.weekday())

emails = create_overview_dinner_emails(users, begindate, enddate) + create_overview_emails(users, begindate, enddate) + create_debt_emails(users)

try:
send_emails(emails)
flash("Emails succesvol verstuurd!", "success")
dbhandler.update_settings('last_overview_email', enddate.strftime("%Y-%m-%d"))
dbhandler.update_settings('last_debt_email', enddate.strftime("%Y-%m-%d"))
except socket.gaierror:
flash("Kon geen verbinding maken met de KVLS Server. Weet je zeker dat de computer internet heeft?", "danger")


def monthlist_fast(dates):
# start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
start, end = dates[0], dates[1]
total_months = lambda dt: dt.month + 12 * dt.year
mlist = []
for tot_m in range(total_months(start) - 1, total_months(end)):
y, m = divmod(tot_m, 12)
mlist.append(datetime(y, m + 1, 1).strftime("%B"))
del mlist[-1]

months = ""
for i in range(0, len(mlist)):
if i == len(mlist) - 1:
months += mlist[i]
elif i == len(mlist) - 2:
months += mlist[i] + " en "
else:
months += mlist[i] + ", "

return months


def create_debt_emails(users):
emails = []

for u in users:
if u.balance < app.config['DEBT_MAXIMUM']:
result = {'html': render_template('email/debt.html', user=u),
'body': render_template('email/debt.txt', user=u),
'recipients': [u.email], 'subject': "Je hebt een te hoge schuld!"}
emails.append(result)

return emails


def create_overview_dinner_emails(users, begindate, enddate):
emails = []

for u in users:
purchases = Purchase.query.filter(
and_(Purchase.product_id == dbhandler.settings['dinner_product_id'], Purchase.user_id == u.id,
Purchase.timestamp > begindate, Purchase.timestamp < enddate)).all()
if len(purchases) > 0:

months = monthlist_fast([begindate, enddate])
total = 0
for p in purchases:
total += p.amount * p.price

result = {'html': render_template('email/overview_dinner.html', user=u, purchases=purchases, total=total,
months=months),
'body': render_template('email/overview_dinner.txt', user=u, purchases=purchases, total=total,
months=months),
'recipients': [u.email], 'subject': "Maandelijks overzicht Stam Opkomstdiner {}".format(months)}
emails.append(result)

return emails


def create_overview_emails(users, begindate, enddate):
emails = []

for u in users:
transactions = Transaction.query.filter(and_(Transaction.user_id == u.id, Transaction.timestamp > begindate, Transaction.timestamp < enddate)).all()
if len(transactions) > 0:
months = monthlist_fast([begindate, enddate])

result = {'html': render_template('email/overview.html', user=u, transactions=transactions, Product=Product,
Purchase=Purchase, Upgrade=Upgrade, months=months),
'body': render_template('email/overview.txt', user=u, transactions=transactions, Product=Product,
Purchase=Purchase, Upgrade=Upgrade, months=months),
'recipients': [u.email], 'subject': "Maandelijks overzicht transacties {}".format(months)}
emails.append(result)

return emails


def send_emails(emails):
with mail.connect() as conn:
for e in emails:
msg = Message(recipients=e['recipients'], html=e['html'], body=e['body'], subject=e['subject'])
conn.send(msg)
17 changes: 16 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from flask import render_template, flash, redirect, url_for, request, abort, jsonify, json, make_response
from sqlalchemy import and_
from app import app, stats, socket, spotify, socketio, dbhandler
from app import app, stats, socket, spotify, socketio, dbhandler, emailhandler
from app.forms import UserRegistrationForm, UpgradeBalanceForm, UserGroupRegistrationForm, DrinkForm, \
ChangeDrinkForm, ChangeDrinkImageForm, AddInventoryForm, PayOutProfitForm, AddQuoteForm, SlideInterruptForm, ChooseSpotifyUser
from app.models import User, Usergroup, Product, Purchase, Upgrade, Transaction, Inventory
Expand Down Expand Up @@ -852,12 +852,27 @@ def throw_exception():
def shutdown():
if request.remote_addr != "127.0.0.1":
abort(403)
if request.args.get('emails') == 'True':
if dbhandler.overview_emails:
emailhandler.send_overview_emails()
elif dbhandler.debt_emails:
emailhandler.send_debt_emails()
shutdown_server()
app.logger.info('Tikker shutting down')
return render_template('error.html', title="Tikker wordt afgesloten...", h1="Uitschakelen",
message="Tikker wordt nu afgesloten. Je kan dit venster sluiten.", gif_url="")


@app.route('/testemail/<int:mail>')
def test_emailing(mail):
if int(mail) == 1:
emailhandler.test_debt_email()
elif int(mail) == 2:
emailhandler.test_overview_email()
elif int(mail) == 3:
emailhandler.test_dinner_overview_email()
return redirect(url_for('index'))

@app.route('/error/403')
def show_401():
message = "Je bezoekt Tikker niet vanaf de computer waar Tikker op is geïnstalleerd. Je hebt daarom geen toegang tot deze pagina."
Expand Down
16 changes: 5 additions & 11 deletions app/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,11 @@
sp = None
current_user = ""
currently_playing_id = ""
SPOTIPY_CLIENT_ID = 'e81cc50c967a4c64a8073d678f7b6503'
SPOTIPY_CLIENT_SECRET = 'c8d84aec8a6d4197b5eca4991ba7694b'
SPOTIPY_REDIRECT_URI = 'http://127.0.0.1:5000/api/spotify/login'
SCOPE = 'user-read-playback-state user-modify-playback-state user-read-currently-playing'
CACHE = '.spotipyoauthcache'
sp_oauth = oauth2.SpotifyOAuth(SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET, SPOTIPY_REDIRECT_URI, scope=SCOPE, cache_path=CACHE)


CACHE = app.config['SPOTIPY_CACHE']
sp_oauth = oauth2.SpotifyOAuth(app.config['SPOTIPY_CLIENT_ID'], app.config['SPOTIPY_CLIENT_SECRET'],
app.config['SPOTIPY_REDIRECT_URI'], scope=app.config['SPOTIPY_SCOPE'], cache_path=CACHE)
history = []


def logout():
global sp, current_user
sp = None
Expand All @@ -33,8 +27,8 @@ def logout():
def set_cache(cache):
global CACHE, sp_oauth
CACHE = cache
sp_oauth = oauth2.SpotifyOAuth(SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET, SPOTIPY_REDIRECT_URI, scope=SCOPE,
cache_path=CACHE)
sp_oauth = oauth2.SpotifyOAuth(app.config['SPOTIPY_CLIENT_ID'], app.config['SPOTIPY_CLIENT_SECRET'],
app.config['SPOTIPY_REDIRECT_URI'], scope=app.config['SPOTIPY_SCOPE'], cache_path=CACHE)
return


Expand Down
4 changes: 1 addition & 3 deletions app/static/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,7 @@ select:focus,
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
width: 150px;
height: 150px;
z-index: 10000;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
Expand Down
27 changes: 25 additions & 2 deletions app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,21 @@ <h5 class="modal-title">Tikker offline</h5>
<h5 class="modal-title">Tikker afsluiten</h5>
</div>
<div class="modal-body">
Weet je zeker dat je Tikker wilt afsluiten?
Weet je zeker dat je Tikker wilt afsluiten?<br><br>

{% if send_overview_emails == true %}
Voor het afsluiten kun je ook iedereen een mail sturen met <b>het maandelijkse transactieoverzicht</b> en een mail met <b>eventuele schulden</b>.
Het versturen van deze mails kan enkele minuten duren.
{% elif send_debt_emails == true %}
Voor het afsluiten kun je ook iedereen met een hoge schuld een mail sturen met <b>de hoogte van de schuld</b>.
Het versturen van deze mails kan enkele minuten duren.
{% endif %}
</div>
<div class="modal-footer">
<a href="/shutdown" type="button" class="btn btn-danger">Afsluiten</a>
{% if send_overview_emails == true or send_debt_emails == true %}
<a href="{{ url_for('shutdown', emails=true) }}" type="button" class="btn btn-danger" onclick="showLoadingBar()">Mailen en afsluiten</a>
{% endif %}
<a href="{{ url_for('shutdown') }}" type="button" class="btn btn-danger">Afsluiten</a>
<button type="button" class="btn btn-success" data-dismiss="modal">Terug</button>
</div>
</div>
Expand All @@ -91,9 +102,21 @@ <h5 class="modal-title">Over Tikker (V{{ version }})</h5>
<h5>Bekende bugs</h5>
<ul>
<li>Wanneer je inlogt met Spotify, log je automatisch in als de eerste persoon die dit deed in de huidige installatie van Tikker</li>
<li>Het keuzemenu bij Tikker BigScreen wie inlogt op Spotify werkt niet</li>
</ul>
<hr>
<h5>Changelog</h5>
<b>Versie 1.4.0 (31/12/2019)</b>: Emails
<ul>
<li>Tikker kan nu emails versturen</li>
<ul>
<li>Elke week wanneer iemands schuld hoger is dan 30 euro</li>
<li>Elke maand wanneer iemand heeft meegegeten bij het opkomstdiner</li>
<li>Elke maand een volledig transactieoverzicht van de afgelopen maand</li>
<li>Mails moeten handmatig verstuurd worden bij het afsluiten van Tikker</li>
</ul>
<li>Agenda slide toegevoegd aan BigScreen</li>
</ul>
<b>Versie 1.3.3 (14/12/2019)</b>: Borrels
<ul>
<li>Borrelmodus toegevoegd</li>
Expand Down
19 changes: 19 additions & 0 deletions app/templates/email/debt.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<p>Beste {{ user.name }},<br></p>
<p>Volgens onze administratie heb je nog een grote openstaande schuld.
Het gaat hierbij om een bedrag van:</p>
<p><span style="color: red; font-weight: bold; font-size: 20px">€ {{ '%.2f' % user.balance }}</span></p>

<p>Zou je deze schuld zo snel mogelijk willen betalen?
Extra overgemaakt geld wordt als saldo op je Tikker account gezet.
Betalen kan door over te maken naar rekeningnummer NL03RABO0333194322 t.n.v. Scouting Kornet van Limburg Stirum o.v.v. Opwaardering met je naam.
Je kunt ook Hans vragen een Tikkie te sturen.</p>

<p><br>
<i>Met vriendelijke Scoutsgroet,</i><br>
<b>Tikker</b><br><br>

<b>Scouting Kornet van Limburg Stirum</b><br>
Hooiberglaan 7 | 8121 RA | Olst <br>
<a href="http://www.kvls.nl/" target="_blank">www.kvls.nl</a><br>
<img border=0 width=83 height=96 src="https://www.kvls.nl/images/images/KVLS-Logo-modern-kleur.png">
</p>
14 changes: 14 additions & 0 deletions app/templates/email/debt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Beste {{ user.name }},

Volgens onze administratie heb je nog een grote openstaande schuld.
Het gaat hierbij om een bedrag van

€ {{ '%.2f' % user.balance }}

Zou je deze schuld zo snel mogelijk willen betalen?
Extra overgemaakt geld wordt als saldo op je Tikker account gezet.
Betalen kan door over te maken naar rekeningnummer NL03RABO0333194322 t.n.v. Scouting Kornet van Limburg Stirum o.v.v. Opwaardering met je naam.
Je kunt ook Hans vragen een Tikkie te sturen.

Met vriendelijke Scoutsgroet,
Tikker
Loading

0 comments on commit 751fbe0

Please sign in to comment.