From 751fbe06ff93bbf624570e01c8e956bfe105a325 Mon Sep 17 00:00:00 2001 From: "Roy (Laptop)" Date: Tue, 31 Dec 2019 16:28:13 +0100 Subject: [PATCH] Added email support to Tikker: sending an email with transaction overview, dinner overview and high debt if necessary. Moved secret passwords and keys to environment variables Updated flask to newest version --- .gitignore | 2 + app/__init__.py | 2 + app/dbhandler.py | 21 +++- app/emailhandler.py | 150 +++++++++++++++++++++++ app/routes.py | 17 ++- app/spotify.py | 16 +-- app/static/css/styles.css | 4 +- app/templates/base.html | 27 +++- app/templates/email/debt.html | 19 +++ app/templates/email/debt.txt | 14 +++ app/templates/email/overview.html | 55 +++++++++ app/templates/email/overview.txt | 15 +++ app/templates/email/overview_dinner.html | 29 +++++ app/templates/email/overview_dinner.txt | 13 ++ config.py | 13 ++ requirements.txt | 3 +- 16 files changed, 381 insertions(+), 19 deletions(-) create mode 100644 app/emailhandler.py create mode 100644 app/templates/email/debt.html create mode 100644 app/templates/email/debt.txt create mode 100644 app/templates/email/overview.html create mode 100644 app/templates/email/overview.txt create mode 100644 app/templates/email/overview_dinner.html create mode 100644 app/templates/email/overview_dinner.txt diff --git a/.gitignore b/.gitignore index b539435..bd2baf1 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/app/__init__.py b/app/__init__.py index 683df90..be150b3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -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 @@ -42,6 +43,7 @@ Bootstrap(app) breadcrumbs = Breadcrumbs(app=app) app.config['BOOTSTRAP_SERVE_LOCAL'] = True +mail = Mail(app) from app import routes, models diff --git a/app/dbhandler.py b/app/dbhandler.py index 7f2019e..ccc49e5 100644 --- a/app/dbhandler.py +++ b/app/dbhandler.py @@ -11,6 +11,8 @@ settings = {} borrel_mode_enabled = False borrel_mode_drinks = [] +overview_emails = False +debt_emails = False def initialize_settings(): @@ -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 = [] @@ -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 diff --git a/app/emailhandler.py b/app/emailhandler.py new file mode 100644 index 0000000..b71474c --- /dev/null +++ b/app/emailhandler.py @@ -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) diff --git a/app/routes.py b/app/routes.py index 9554c53..6495958 100644 --- a/app/routes.py +++ b/app/routes.py @@ -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 @@ -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/') +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." diff --git a/app/spotify.py b/app/spotify.py index 1fb2f84..c292f3b 100644 --- a/app/spotify.py +++ b/app/spotify.py @@ -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 @@ -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 diff --git a/app/static/css/styles.css b/app/static/css/styles.css index 022e0c6..dd1ac8a 100644 --- a/app/static/css/styles.css +++ b/app/static/css/styles.css @@ -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%; diff --git a/app/templates/base.html b/app/templates/base.html index 4b6bcca..2419abe 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -63,10 +63,21 @@ @@ -91,9 +102,21 @@
Bekende bugs

Changelog
+ Versie 1.4.0 (31/12/2019): Emails + Versie 1.3.3 (14/12/2019): Borrels