Skip to content

Instantly share code, notes, and snippets.

@alexpogue
Last active May 22, 2019 12:04
Show Gist options
  • Save alexpogue/ce29856bb4d5eb285747addb5f10e967 to your computer and use it in GitHub Desktop.
Save alexpogue/ce29856bb4d5eb285747addb5f10e967 to your computer and use it in GitHub Desktop.
from facebook import get_user_from_cookie, GraphAPI
from flask import Flask, g, render_template, session, request, redirect, url_for, abort
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
import datetime
import dateutil.parser
import calendar
from enum import Enum
import json
app = Flask(__name__)
app.config.from_pyfile('../config.py')
db = SQLAlchemy(app)
db.init_app(app)
from .models import User, Bet, BetEvent, Moderation, WagerEncoder
#db.drop_all()
db.create_all()
# Facebook app details
FB_APP_ID = app.config["FB_APP_ID"]
FB_APP_NAME = app.config["FB_APP_NAME"]
FB_APP_SECRET = app.config["FB_APP_SECRET"]
@app.route('/')
def index():
# If a user was set in the get_current_user function before the request,
# the user is logged in.
if g.user:
num_events = 5
user = User.query.get(g.user['id'])
return render_template('index.html', app_id=FB_APP_ID,
app_name=FB_APP_NAME, user=user, bet_events=__get_bet_events(num_events), get_betevent_state=get_betevent_state, BetEventState=BetEventState, assignPoints=assignPoints())
# Otherwise, a user is not logged in.
return render_template('login.html', app_id=FB_APP_ID, name=FB_APP_NAME)
@app.route('/logout')
def logout():
"""Log out the user from the application.
Log out the user from the application by removing them from the
session. Note: this does not log the user out of Facebook - this is done
by the JavaScript SDK.
"""
session.pop('user', None)
return redirect(url_for('index'))
@app.route('/bet')
def bet_page():
if g.user:
user = User.query.get(g.user['id'])
return render_template('betting-page.html', app_id=FB_APP_ID, name=FB_APP_NAME, user=user, bet_events=__get_bet_events(), BetEventState=BetEventState, get_betevent_state=get_betevent_state,assignPoints=assignPoints())
return redirect(url_for('index'))
@app.route('/moderate')
def moderate_page():
if g.user:
user = User.query.get(g.user['id'])
return render_template('moderating-page.html', app_id=FB_APP_ID, name=FB_APP_NAME, user=user, bet_events=__get_bet_events(), BetEventState=BetEventState, get_betevent_state=get_betevent_state,assignPoints=assignPoints())
return redirect(url_for('index'))
@app.route('/flag')
def flag_page():
if g.user:
user = User.query.get(g.user['id'])
return render_template('flagging-page.html', app_id=FB_APP_ID, name=FB_APP_NAME, user=user, bet_events=__get_bet_events(), BetEventState=BetEventState, get_betevent_state=get_betevent_state,assignPoints=assignPoints())
return redirect(url_for('index'))
def __get_bet_events(limit=None):
return BetEvent.query.order_by(BetEvent.view_count.desc()).limit(limit).all()
def get_bet_events():
return BetEvent.query.filter_by(hasBeenDistributed=0, status="closed").all()
@app.route('/new-bet-event')
def bet_event_page():
return render_template('new-bet-event.html')
def __get_user(user_id):
return User.query.get(user_id)
@app.route('/api/user/<user_id>')
def get_user(user_id):
user = __get_user(user_id)
# TODO: get rid of this - we seem to need it to have 'betevents' be included in json output - without it, throws error
print("user = {}".format(user))
if not user:
return "", 404
else:
return json.dumps(user, cls=WagerEncoder)
class BetEventState(Enum):
not_yet_created= 1
betting = 2
moderating = 3
flagging = 4
closed = 5
def get_betevent_state(betevent):
now = datetime.datetime.utcnow()
return get_betevent_state_time(betevent, now)
def get_betevent_state_time(betevent, now):
#moderating_hours = 2
moderating_minutes = 15
#flagging_minutes = 15
flagging_minutes = 1
#moderation_end_time = betevent.end_time + datetime.timedelta(hours=moderating_hours)
moderation_end_time = betevent.end_time + datetime.timedelta(minutes=moderating_minutes)
flagging_end_time = moderation_end_time + datetime.timedelta(minutes=flagging_minutes)
if now < betevent.created:
return BetEventState.not_yet_created
elif now < betevent.end_time:
betevent.status = "betting"
db.session.commit()
return BetEventState.betting
elif now < moderation_end_time:
betevent.status = "moderating"
db.session.commit()
return BetEventState.moderating
elif now < flagging_end_time:
betevent.status = "flagging"
db.session.commit()
return BetEventState.flagging
else:
betevent.status = "closed"
db.session.commit()
return BetEventState.closed
@app.route('/api/moderate', methods=['PUT'])
def moderate():
if request.is_json:
print("detected request is json")
data = request.get_json()
betevent_id=data['betevent']
option = data['option']
betevent = BetEvent.query.filter(BetEvent.id == betevent_id).first()
if get_betevent_state(betevent) != BetEventState.moderating:
abort(400, {'data': 'Can\'t moderate a BetEvent that\'s not in moderating state'})
user = User.query.get(g.user['id'])
#user = User.query.get(1) # for testing for iOS before we get facebook login
if betevent is None:
abort(400, {'data': 'Could not find BetEvent with id {}'.format(betevent_id)})
# Look up existing moderation to update if it's there
moderation = Moderation.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
print("moderation = {}".format(moderation))
if moderation is None:
moderation = Moderation(users_id=user.id, option=option)
moderation.betevent = betevent
user.moderations.append(moderation)
db.session.add(user)
db.session.commit()
return json.dumps({'data': 'stored'})
else:
moderation = Moderation.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
moderation.option = option
db.session.commit()
return json.dumps({'data': 'updated'})
@app.route('/api/bet', methods=['GET', 'PUT'])
def place_bet():
if request.method == 'PUT':
print(request.data)
if request.is_json:
print("detected request is json")
data = request.get_json()
betevent_id=data['betevent']
amount = data['amount']
option = data['option']
else:
print("detected request not json - treating as form")
betevent_id=request.form['betevent']
amount = request.form['amount']
option = request.form['option']
betevent = BetEvent.query.filter(BetEvent.id == betevent_id).first()
if get_betevent_state(betevent) != BetEventState.betting:
abort(400, {'message': 'Can\'t bet on BetEvent that\'s not in betting state'})
if betevent is None:
abort(404)
user = User.query.get(g.user['id'])
# user = User.query.get(1) # for testing for iOS before we get facebook login
#The case where the user doesn't have enough points
#If they don't have enough points we need to send them a message through Ajax that tells them they can't bet that many points.
bet = Bet.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
if bet is None:
if user.points < int(amount):
errorMessage = "Insufficient points"
return json.dumps({'data':errorMessage})
else:
if(user.points + bet.amount < int(amount)):
errorMessage = "Insufficient points"
return json.dumps({'data':errorMessage})
#The case where the user has enough points
#If they have enough points we want to subtract the amount from their total points in the database
if int(amount) <= 0:
errorMessage = "Yo bro, you can't bet nothing"
return json.dumps({'data':errorMessage})
# Look up existing bet to update if it's there
bet = Bet.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
if bet is None:
bet = Bet(users_id=user.id, amount=amount, option=option)
bet.betevent = betevent
user.points -= int(amount)
betevent.totalAmount += int(amount)
user.betevents.append(bet)
db.session.add(user)
db.session.commit()
return json.dumps({'data': ''})
else:
bet = Bet.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
#Get the old amount
oldAmount = bet.amount
#Update the new amount
bet.amount = amount
#Add the old amount back to the user's points in the database
user.points += oldAmount
betevent.totalAmount -=oldAmount
#subtract out the new amount
user.points -= int(amount)
betevent.totalAmount += int(amount)
#save changes
bet.option = option
db.session.commit()
return json.dumps({'data': ''})
elif request.method == 'GET':
bets = Bet.query.all()
return json.dumps({'results': bets}, cls=WagerEncoder)
@app.route('/api/betevent', methods=['GET', 'POST'])
def bet_events():
if request.method == 'POST':
if request.is_json:
print("detected request is json")
data = request.get_json()
name=data['name']
description=data['description']
end_datetime_str = data['beteventend']
else:
print("detected request not json - treating as form")
name = request.form.getlist('beteventname')[0]
description = request.form.getlist('beteventdescription')[0]
end_datetime_str = request.form.getlist('beteventend')[0]
#print("datebeforeparsing= {}".format(end_datetime_str))
end_datetime = dateutil.parser.parse(end_datetime_str)
#print("date = {}".format(end_datetime.isoformat()))
event = BetEvent(name=name, description=description, end_time=end_datetime)
app.logger.info(event.id)
db.session.add(event)
db.session.commit()
app.logger.info(event.id)
return redirect(url_for('bet_event_view', bet_event_id=event.id))
elif request.method == 'GET':
bet_events = __get_bet_events()
for event in bet_events:
# TODO: get rid of this - we seem to need it to have 'betters' be included in json output - without it, throws error
print("bet_event = {}".format(event))
return json.dumps({'results': bet_events}, cls=WagerEncoder)
def __get_bet_event(bet_event_id):
return BetEvent.query.get(bet_event_id)
def __update_bet_event(bet_event_id, updates):
db.session.query(BetEvent).filter_by(id=bet_event_id).update(updates)
@app.route('/api/betevent/<bet_event_id>', methods=['GET', 'PUT'])
def bet_event(bet_event_id):
if request.method == 'GET':
bet_event = __get_bet_event(bet_event_id)
# TODO: get rid of this - we seem to need it to have 'betters' be included in json output - without it, throws error
print("bet_event = {}".format(bet_event))
if not bet_event:
return "", 404
else:
return json.dumps(bet_event, cls=WagerEncoder)
elif request.method == 'PUT':
if request.is_json:
print("detected request is json")
data = request.get_json()
state_str = data['state']
if state_str is not None:
state_enum = BetEventState[state_str]
if state_enum is None:
print("Error: couldn't find enum called {}".format(state_str))
return "", 500
event = __get_bet_event(bet_event_id)
if event is None:
return "", 404
__update_bet_event(bet_event_id, updates={"state": state_enum})
db.session.commit()
return "success", 200
else:
print("Error: expected json input to /api/betevent/<bet_event_id>")
def datetime_to_utc(dt):
return calendar.timegm(dt.utctimetuple())
# get a single bet event
@app.route('/betevent/<bet_event_id>')
def bet_event_view(bet_event_id):
bet_event = __get_bet_event(bet_event_id)
if not bet_event:
return "", 404
else:
user = User.query.get(g.user['id'])
bet_event.view_count = bet_event.view_count + 1
db.session.commit()
return render_template('bet-event-view.html', bet_event=bet_event, user=user, BetEventState=BetEventState, betevent_state=get_betevent_state(bet_event), assignPoints=assignPoints())
def assignPoints():
#First we have to get a list of the bet events
betEvents = get_bet_events()
#if len(betEvents) == 0:
# print("No bet events to show!")
#while betEvents is not empty
while len(betEvents) != 0:
#pop the first bet event
event = betEvents.pop(0)
#extract the bet_event_id, the list of betters, the total amount on the bet event, and the list of moderators
betEventId = event.id
betters = event.betters
totalAmount = event.totalAmount
moderators = event.moderators
print()
print()
print("The bet id is: " + str(betEventId))
print("The list of betters are: " + str(betters))
print("The total amount of the event is: " + str(totalAmount))
print("The list of moderators are: " + str(moderators))
#Completed: determine the option by checking through the list of moderators and seeing what the popular option was (could do ratio, ect)
#We need to make moderate work first before doing the step above
#TODO: Add points to people's acounts for moderating a winning event.
winningOption = 0
zeroCount = 0
oneCount = 0
if len(moderators) != 0:
for mod in moderators:
if mod.option == 0:
zeroCount+=1
else:
oneCount+=1
print("The number of moderators that chose option 0 are: " + str(zeroCount))
print("The number of moderators that chose option 1 are: " + str(oneCount))
if zeroCount > oneCount:
payModerators(moderators,0)
winningOption = 0
elif zeroCount < oneCount:
payModerators(moderators,1)
winningOption = 1
else:
#Right now, we will just set the winning option to 0 when they're equal. I will try and figure out what to do with this later.
winningOption = 0
winningAmount = 0
print("The winning option is: " + str(winningOption))
#*BETS
for better in betters:
if(better.option == winningOption):
betterId = better.users_id
winningAmount+= better.amount
print("The better id's are: " + str(betterId))
print("The winning amount is: " + str(winningAmount))
for better in betters:
if(better.option == winningOption):
betterId = better.users_id
payOut = (better.amount / winningAmount) * totalAmount
print("The payout for: " + str(better.better.name + " is: " + str(payOut)))
user = User.query.get(betterId)
user.points += payOut
event.hasBeenDistributed = 1
db.session.commit()
print()
print()
#for each better, extract the amount and calculate how much of the winnings for that event they get, and add it to their User table
#Move on to the next event.
#Distribute points, and shift the bit from 0 to 1
return u'success'
def payModerators(moderators, option):
for moderator in moderators:
modUserId = moderator.users_id
if moderator.option == option:
user = User.query.get(modUserId)
user.points +=10
db.session.commit()
@app.before_request
def get_current_user():
"""Set g.user to the currently logged in user.
Called before each request, get_current_user sets the global g.user
variable to the currently logged in user. A currently logged in user is
determined by seeing if it exists in Flask's session dictionary.
If it is the first time the user is logging into this application it will
create the user and insert it into the database. If the user is not logged
in, None will be set to g.user.
"""
# Set the user in the session dictionary as a global g.user and bail out
# of this function early.
if session.get('user'):
g.user = session.get('user')
return
# Attempt to get the short term access token for the current user.
result = get_user_from_cookie(cookies=request.cookies, app_id=FB_APP_ID,
app_secret=FB_APP_SECRET)
# If there is no result, we assume the user is not logged in.
if result:
# Check to see if this user is already in our database.
user = User.query.filter(User.fb_id == result['uid']).first()
if not user:
# Not an existing user so get info
graph = GraphAPI(result['access_token'])
profile = graph.get_object('me')
if 'link' not in profile:
profile['link'] = ""
# Create the user and insert it into the database
user = User(fb_id=str(profile['id']), name=profile['name'],
profile_url=profile['link'],
access_token=result['access_token'])
db.session.add(user)
elif user.access_token != result['access_token']:
# If an existing user, update the access token
user.access_token = result['access_token']
# Commit changes to the database
db.session.commit()
# Add the user to the current session
session['user'] = dict(id=user.id, name=user.name, profile_url=user.profile_url,
fb_id=user.fb_id, points=user.points,access_token=user.access_token)
# set the user as a global g.user
g.user = session.get('user', None)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment