import random import operator import json import datetime from flask import Flask, Response, redirect, url_for, request, abort, render_template from flask_login import LoginManager, UserMixin, login_required, login_user, logout_user, current_user import sqlalchemy from sqlalchemy.sql import select, text from dateutil.parser import parse app = Flask(__name__) #config app.config.update( DEBUG = True, SECRET_KEY = 'secret_xxx', SESSION_TYPE = 'memcached' ) db_engine = {} db_metadata = {} meals = {} users = {} KUK_POINTS = [0, 0, 52, 69, 80, 90, 102, 112, 120, 126] LAZY_KUK_POINTS = [0, 0, 38, 48, 56, 65, 72, 77, 80, 81] class User(UserMixin): def __init__(self, name): self.name = name self.id = name self.user_id = -1 @staticmethod def get(name): if name not in users: users[name] = User(name) return users[name] def connect(user, password, db, host='localhost', port=5432): url = 'postgresql://{}:{}@{}:{}/{}' url = url.format(user, password, host, port, db) con = sqlalchemy.create_engine(url, client_encoding='utf8') meta = sqlalchemy.MetaData(bind=con, reflect=True) return con, meta def getLeaderboard(): s = 'select u.username as name, u.score as score, max(m.meal_date) as last_meal from users as u left join meals as m on m.kuk = u.id where u.id > 0 group by u.id order by score, last_meal;' r = db_engine.execute(text(s)) leaders=[] for row in r: leaders.append(dict(row)) r.close() return leaders def getUpcomingMeals(): # meals = [] # meals.append({'id':1, 'kuk':'davide', 'eaters':['marek','sven','wille'], 'flavor_text':'Pizza', 'date':'Tuesday 21st of November', 'selection_deadline':'23 hours 14 minutes', 'status':0}) # meals.append({'id':2, 'kuk':'marek', 'eaters':['davide','sven','david', 'wille'], 'flavor_text':'Grzybowa', 'date':'Friday 23rd of November', 'selection_deadline':'3 Days 23 hours', 'status':0}) # meals.append({'id':2, 'kuk':'marek', 'eaters':['davide','sven','david', 'wille'], 'flavor_text':'Grzybowa', 'date':'Friday 23rd of November', 'selection_deadline':'3 Days 23 hours', 'status':0}) # meals.append({'id':2, 'kuk':'marek', 'eaters':['davide','sven','david', 'wille'], 'flavor_text':'Grzybowa', 'date':'Sunday 25rd of November', 'selection_deadline':'3 Days 23 hours', 'status':0}) # meals.append({'id':2, 'kuk':'marek', 'eaters':['davide','sven','david', 'wille'], 'flavor_text':'Grzybowa', 'date':'Sunday 25rd of November', 'selection_deadline':'3 Days 23 hours', 'status':0}) # meals.append({'id':2, 'kuk':'marek', 'eaters':['davide','sven','david', 'wille'], 'flavor_text':'Grzybowa', 'date':'Monday 26rd of November', 'selection_deadline':'3 Days 23 hours', 'status':0}) query = 'SELECT foo.id, username AS kuk, meal_date, array_agg AS eaters, selection_deadline, is_lazy FROM (SELECT is_lazy, meals.id, kuk, meal_date, selection_deadline, array_agg(username) FROM meals JOIN users ON users.id = any(meals.eaters) WHERE meals.status=0 GROUP BY meals.id ORDER BY meal_date) AS foo JOIN users ON kuk=users.id;' r = db_engine.execute(text(query)) meals = [] leaderboard = getLeaderboard() for row in r: meals.append(dict(row)) seconds_to_deadline = (meals[-1]['selection_deadline'] - datetime.datetime.now()).total_seconds() if seconds_to_deadline < 0: meals[-1]['friendly_selection_deadline'] = '' elif seconds_to_deadline > 3600: hours_td = round(seconds_to_deadline / 3600) days_td = int(hours_td / 24) hours_td = hours_td % 24 meals[-1]['friendly_selection_deadline'] = str(days_td) + ' days ' + str(hours_td) + ' hours' else: meals[-1]['friendly_selection_deadline'] = str(round(seconds_to_deadline / 60)) + ' minutes' meals[-1]['meal_time'] = meals[-1]['meal_date'].strftime('%H:%M') meals[-1]['meal_friendly_date'] = meals[-1]['meal_date'].strftime('%A %d %B') meals[-1]['candidate'] = [l['name'] for l in leaderboard if l['name'] in meals[-1]['eaters']][0] if (meals[-1]['meal_date'] - datetime.datetime.now()).total_seconds() < 0: meals[-1]['needs_confirmation'] = True else: meals[-1]['needs_confirmation'] = False leaderboard = applyTempMeal(leaderboard, meals[-1]) r.close() return meals, leaderboard def applyTempMeal(leaderboard, meal): ld = leaderboard[:] m = meal.copy() if m['kuk'] == '': m['kuk'] = m['candidate'] m['eaters'].remove(m['candidate']) for i in range(len(ld)): if ld[i]['name'] == m['kuk']: if m['is_lazy']: ld[i]['score'] += LAZY_KUK_POINTS[len(m['eaters'])] else: ld[i]['score'] += KUK_POINTS[len(m['eaters'])] ld[i]['last_meal'] = m['meal_date'] elif ld[i]['name'] in m['eaters']: if m['is_lazy']: ld[i]['score'] -= LAZY_KUK_POINTS[len(m['eaters'])] / len(m['eaters']) else: ld[i]['score'] -= KUK_POINTS[len(m['eaters'])] / len(m['eaters']) return sorted(ld, key=lambda i:(i['score'], i['last_meal'] or datetime.datetime(1970, 1, 1, 0, 0))) def getMeal(meal_id): query = 'SELECT foo.id, username AS kuk, meal_date, array_agg AS eaters, selection_deadline FROM (SELECT meals.id, kuk, meal_date, selection_deadline, array_agg(username) FROM meals JOIN users ON users.id = any(meals.eaters) WHERE meals.id=:meal_id GROUP BY meals.id ORDER BY meal_date) AS foo JOIN users ON kuk=users.id;' r = db_engine.execute(text(query), meal_id=meal_id) meals = [] for row in r: meals.append(dict(row)) r.close() if len(meals) == 0: return None meal = meals[0] seconds_to_deadline = (meal['selection_deadline'] - datetime.datetime.now()).total_seconds() if seconds_to_deadline < 0: meal['friendly_selection_deadline'] = '' elif seconds_to_deadline > 3600: hours_td = round(seconds_to_deadline / 3600) days_td = int(hours_td / 24) hours_td = hours_td % 24 meal['friendly_selection_deadline'] = str(days_td) + ' days ' + str(hours_td) + ' hours' else: meal['friendly_selection_deadline'] = str(round(seconds_to_deadline / 60)) + ' minutes' meal['meal_time'] = meal['meal_date'].strftime('%H:%M') meal['meal_friendly_date'] = meal['meal_date'].strftime('%A %d %B') meal['candidate'] = 'davide' if (meal['meal_date'] - datetime.datetime.now()).total_seconds() < 0: meal['needs_confirmation'] = True else: meal['needs_confirmation'] = False return meal # adding to meal @app.route("/addme") @login_required def addme(): meal_id = request.args.get('meal') query = 'SELECT :user_id = any (eaters) AS is_eater FROM meals WHERE id =:meal_id;' r = db_engine.execute(text(query), user_id=current_user.user_id, meal_id = meal_id) results = [] for row in r: results.append(dict(row)) if not results[0]['is_eater'] == 'True': query = "UPDATE meals SET eaters = array_cat(eaters, '{:user_id}') where id=:meal_id;" r = db_engine.execute(text(query), user_id=current_user.user_id, meal_id = meal_id) return render_template('appresponse.html', message='Have a nice meal ' + meal_id + " mister " + str(current_user.name) ) else: return render_template('appresponse.html', message='You are already a registered eater') @app.route("/removeme") @login_required def removeme(): meal_id = request.args.get('meal') query = 'UPDATE meals SET eaters = array_remove(eaters, :user_id) WHERE id = :meal_id;' r = db_engine.execute(text(query), user_id=current_user.user_id, meal_id = meal_id) return render_template('appresponse.html', message='You were removed from that meal') @app.route("/planmeal", methods=['POST']) @login_required def plan_meal(): if request.form['kuk'] == None: return render_template('appresponse.html', message='Invalid request. Nothing to do here') query = "insert into meals(kuk, eaters, flavor_text, meal_date, selection_deadline, status) values(:kuk, ':eaters', ':flavour_text', ':meal_date', ':selection_deadline', 0);" # correct formating example: # insert into meals(kuk, eaters, flavor_text, meal_date, selection_deadline, status) values(2, '{1,3,4}', 'yum', '2018-12-25 18:22:12', '2018-12-25 10:22:12', 0); r = db_engine.execute(text(query), kuk=request.form['kuk'], eaters=request.form['eaters'], flavour_text=request.form['flavour_text'], meal_date=request.form['meal_date'], selection_deadline=request.form['selection_deadline'] ) return render_template('appresponse.html', message='The meal is planned') @app.route('/') def index(): meals, _ = getUpcomingMeals() return render_template('meal_list.html', leaderboard=getLeaderboard(), meals=meals) @app.route('/viewmeal') def view_meal(): meal_id = request.args.get('meal') meal = getMeal(meal_id) if meal == None: return abort(400) print(meal) return render_template('view_meal.html', leaderboard=getLeaderboard(), meal=meal) @app.route('/mealplanner') @login_required def meal_planner(): return render_template('meal_planner.html') @app.route('/searchusers') def search_users(): q = request.args.get('q') s = request.args.get('s') query = 'SELECT username FROM users WHERE id>0 AND username LIKE :q;' if s == '1': r = db_engine.execute(text(query), q=(q+'%')) else: r = db_engine.execute(text(query), q=q) result = [] for row in r: result.append(list(row)[0]) r.close() return str(result) @app.route('/getkukcandidate') def get_kuk_candidate(): date = datetime.datetime.strptime(request.args.get('date'), '%Y-%m-%d %H:%M') meal_eaters = request.args.get('eaters').split(',') u_meals, t_lb = getUpcomingMeals() return [e['name'] for e in t_lb if e['name'] in meal_eaters][0] @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] r = db_engine.execute(text('select id from users where username=:username and password=:password'), username=username, password=password) result = [] for row in r: result.append(dict(row)) r.close() print(len(users)) if len(result) > 0: u = User.get(username) u.user_id = result[0]['id'] login_user(u) return redirect(request.args.get('next') or url_for('index')) return abort(401) else: return Response('''

''') with open('admin.json') as f: postgres_credentials = json.load(f) db_engine, db_metadata = connect(postgres_credentials['username'], postgres_credentials['password'], 'kuk_app') meals = sqlalchemy.Table('meals', db_metadata) login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = "login" # somewhere to logout @app.route("/logout") @login_required def logout(): logout_user() return Response('

Logged out

') # handle login failed @app.errorhandler(401) def page_not_found(e): return Response('

Login failed

') # callback to reload the user object @login_manager.user_loader def load_user(userid): return User.get(userid) if __name__ == '__main__': app.run('0.0.0.0')