You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
11 KiB
278 lines
11 KiB
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('''
|
|
<form action="" method="post">
|
|
<p><input type=text name=username>
|
|
<p><input type=password name=password>
|
|
<p><input type=submit value=Login>
|
|
</form>
|
|
''')
|
|
|
|
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('<p>Logged out</p>')
|
|
|
|
# handle login failed
|
|
@app.errorhandler(401)
|
|
def page_not_found(e):
|
|
return Response('<p>Login failed</p>')
|
|
|
|
# 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') |