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 ' )