· 6 years ago · Jun 02, 2019, 07:12 AM
1from facebook import get_user_from_cookie, GraphAPI
2from flask import Flask, g, render_template, session, request, redirect, url_for, abort
3from flask_sqlalchemy import SQLAlchemy
4from sqlalchemy.sql import func
5import datetime
6import dateutil.parser
7import calendar
8from enum import Enum
9import json
10app = Flask(__name__)
11app.config.from_pyfile('../config.py')
12
13db = SQLAlchemy(app)
14db.init_app(app)
15
16from .models import User, Bet, BetEvent, Moderation, WagerEncoder
17
18#db.drop_all()
19db.create_all()
20
21# Facebook app details
22FB_APP_ID = app.config["FB_APP_ID"]
23FB_APP_NAME = app.config["FB_APP_NAME"]
24FB_APP_SECRET = app.config["FB_APP_SECRET"]
25
26@app.route('/')
27def index():
28 # If a user was set in the get_current_user function before the request,
29 # the user is logged in.
30 if g.user:
31 num_events = 5
32 user = User.query.get(g.user['id'])
33 return render_template('index.html', app_id=FB_APP_ID,
34 app_name=FB_APP_NAME, user=user, bet_events=__get_bet_events(num_events), get_betevent_state=get_betevent_state, BetEventState=BetEventState, assignPoints=assignPoints())
35 # Otherwise, a user is not logged in.
36 return render_template('login.html', app_id=FB_APP_ID, name=FB_APP_NAME)
37
38@app.route('/logout')
39def logout():
40 """Log out the user from the application.
41
42 Log out the user from the application by removing them from the
43 session. Note: this does not log the user out of Facebook - this is done
44 by the JavaScript SDK.
45 """
46
47 session.pop('user', None)
48 return redirect(url_for('index'))
49
50@app.route('/bet')
51def bet_page():
52 if g.user:
53 user = User.query.get(g.user['id'])
54 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())
55
56 return redirect(url_for('index'))
57
58@app.route('/moderate')
59def moderate_page():
60 if g.user:
61 user = User.query.get(g.user['id'])
62 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())
63
64 return redirect(url_for('index'))
65
66@app.route('/flag')
67def flag_page():
68 if g.user:
69 user = User.query.get(g.user['id'])
70 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())
71
72 return redirect(url_for('index'))
73
74
75def __get_bet_events(limit=None):
76 return BetEvent.query.order_by(BetEvent.view_count.desc()).limit(limit).all()
77def get_bet_events():
78 return BetEvent.query.filter_by(hasBeenDistributed=0, status="closed").all()
79
80@app.route('/new-bet-event')
81def bet_event_page():
82 return render_template('new-bet-event.html')
83
84def __get_user(user_id):
85 return User.query.get(user_id)
86
87@app.route('/api/user/<user_id>')
88def get_user(user_id):
89 user = __get_user(user_id)
90
91 # TODO: get rid of this - we seem to need it to have 'betevents' be included in json output - without it, throws error
92 print("user = {}".format(user))
93 if not user:
94 return "", 404
95 else:
96 return json.dumps(user, cls=WagerEncoder)
97
98class BetEventState(Enum):
99 not_yet_created= 1
100 betting = 2
101 moderating = 3
102 flagging = 4
103 closed = 5
104
105def get_betevent_state(betevent):
106 now = datetime.datetime.utcnow()
107 return get_betevent_state_time(betevent, now)
108
109def get_betevent_state_time(betevent, now):
110 #moderating_hours = 2
111 moderating_minutes = 15
112 #flagging_minutes = 15
113 flagging_minutes = 1
114
115 #moderation_end_time = betevent.end_time + datetime.timedelta(hours=moderating_hours)
116 moderation_end_time = betevent.end_time + datetime.timedelta(minutes=moderating_minutes)
117 flagging_end_time = moderation_end_time + datetime.timedelta(minutes=flagging_minutes)
118
119 if now < betevent.created:
120 return BetEventState.not_yet_created
121
122 elif now < betevent.end_time:
123 betevent.status = "betting"
124 db.session.commit()
125 return BetEventState.betting
126
127 elif now < moderation_end_time:
128 betevent.status = "moderating"
129 db.session.commit()
130 return BetEventState.moderating
131
132 elif now < flagging_end_time:
133 betevent.status = "flagging"
134 db.session.commit()
135 return BetEventState.flagging
136
137 else:
138 betevent.status = "closed"
139 db.session.commit()
140 return BetEventState.closed
141
142
143@app.route('/api/moderate', methods=['PUT'])
144def moderate():
145 if request.is_json:
146 print("detected request is json")
147 data = request.get_json()
148 betevent_id=data['betevent']
149 option = data['option']
150
151 betevent = BetEvent.query.filter(BetEvent.id == betevent_id).first()
152 if get_betevent_state(betevent) != BetEventState.moderating:
153 abort(400, {'data': 'Can\'t moderate a BetEvent that\'s not in moderating state'})
154
155 user = User.query.get(g.user['id'])
156 #user = User.query.get(1) # for testing for iOS before we get facebook login
157
158 if betevent is None:
159 abort(400, {'data': 'Could not find BetEvent with id {}'.format(betevent_id)})
160
161 # Look up existing moderation to update if it's there
162 moderation = Moderation.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
163 print("moderation = {}".format(moderation))
164 if moderation is None:
165 moderation = Moderation(users_id=user.id, option=option)
166 moderation.betevent = betevent
167 user.moderations.append(moderation)
168 db.session.add(user)
169 db.session.commit()
170 return json.dumps({'data': 'stored'})
171 else:
172 moderation = Moderation.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
173 moderation.option = option
174 db.session.commit()
175 return json.dumps({'data': 'updated'})
176
177
178@app.route('/api/bet', methods=['GET', 'PUT'])
179def place_bet():
180 if request.method == 'PUT':
181 print(request.data)
182 if request.is_json:
183 print("detected request is json")
184 data = request.get_json()
185 betevent_id=data['betevent']
186 amount = data['amount']
187 option = data['option']
188 else:
189 print("detected request not json - treating as form")
190 betevent_id=request.form['betevent']
191 amount = request.form['amount']
192 option = request.form['option']
193
194
195 betevent = BetEvent.query.filter(BetEvent.id == betevent_id).first()
196
197 if get_betevent_state(betevent) != BetEventState.betting:
198 abort(400, {'message': 'Can\'t bet on BetEvent that\'s not in betting state'})
199 if betevent is None:
200 abort(404)
201
202 user = User.query.get(g.user['id'])
203 # user = User.query.get(1) # for testing for iOS before we get facebook login
204
205 #The case where the user doesn't have enough points
206 #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.
207 bet = Bet.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
208
209 if bet is None:
210 if user.points < int(amount):
211 errorMessage = "Insufficient points"
212 return json.dumps({'data':errorMessage})
213 else:
214 if(user.points + bet.amount < int(amount)):
215 errorMessage = "Insufficient points"
216 return json.dumps({'data':errorMessage})
217 #The case where the user has enough points
218 #If they have enough points we want to subtract the amount from their total points in the database
219 if int(amount) <= 0:
220 errorMessage = "Yo bro, you can't bet nothing"
221 return json.dumps({'data':errorMessage})
222
223 # Look up existing bet to update if it's there
224 bet = Bet.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
225
226 if bet is None:
227 bet = Bet(users_id=user.id, amount=amount, option=option)
228 bet.betevent = betevent
229 user.points -= int(amount)
230 betevent.totalAmount += int(amount)
231 user.betevents.append(bet)
232 db.session.add(user)
233 db.session.commit()
234 return json.dumps({'data': ''})
235 else:
236 bet = Bet.query.filter_by(users_id=user.id, betevents_id=betevent_id).first()
237 #Get the old amount
238 oldAmount = bet.amount
239 #Update the new amount
240 bet.amount = amount
241 #Add the old amount back to the user's points in the database
242 user.points += oldAmount
243 betevent.totalAmount -=oldAmount
244 #subtract out the new amount
245 user.points -= int(amount)
246 betevent.totalAmount += int(amount)
247 #save changes
248 bet.option = option
249 db.session.commit()
250 return json.dumps({'data': ''})
251
252 elif request.method == 'GET':
253 bets = Bet.query.all()
254 return json.dumps({'results': bets}, cls=WagerEncoder)
255
256@app.route('/api/betevent', methods=['GET', 'POST'])
257def bet_events():
258 if request.method == 'POST':
259 if request.is_json:
260 print("detected request is json")
261 data = request.get_json()
262 name=data['name']
263 description=data['description']
264 end_datetime_str = data['beteventend']
265 else:
266 print("detected request not json - treating as form")
267 name = request.form.getlist('beteventname')[0]
268 description = request.form.getlist('beteventdescription')[0]
269 end_datetime_str = request.form.getlist('beteventend')[0]
270
271 #print("datebeforeparsing= {}".format(end_datetime_str))
272
273 end_datetime = dateutil.parser.parse(end_datetime_str)
274 #print("date = {}".format(end_datetime.isoformat()))
275
276 event = BetEvent(name=name, description=description, end_time=end_datetime)
277 app.logger.info(event.id)
278
279 db.session.add(event)
280 db.session.commit()
281
282 app.logger.info(event.id)
283 return redirect(url_for('bet_event_view', bet_event_id=event.id))
284 elif request.method == 'GET':
285 bet_events = __get_bet_events()
286 for event in bet_events:
287 # TODO: get rid of this - we seem to need it to have 'betters' be included in json output - without it, throws error
288 print("bet_event = {}".format(event))
289 return json.dumps({'results': bet_events}, cls=WagerEncoder)
290
291def __get_bet_event(bet_event_id):
292 return BetEvent.query.get(bet_event_id)
293
294def __update_bet_event(bet_event_id, updates):
295 db.session.query(BetEvent).filter_by(id=bet_event_id).update(updates)
296
297@app.route('/api/betevent/<bet_event_id>', methods=['GET', 'PUT'])
298def bet_event(bet_event_id):
299 if request.method == 'GET':
300 bet_event = __get_bet_event(bet_event_id)
301 # TODO: get rid of this - we seem to need it to have 'betters' be included in json output - without it, throws error
302 print("bet_event = {}".format(bet_event))
303 if not bet_event:
304 return "", 404
305 else:
306 return json.dumps(bet_event, cls=WagerEncoder)
307 elif request.method == 'PUT':
308 if request.is_json:
309 print("detected request is json")
310 data = request.get_json()
311 state_str = data['state']
312 if state_str is not None:
313 state_enum = BetEventState[state_str]
314 if state_enum is None:
315 print("Error: couldn't find enum called {}".format(state_str))
316 return "", 500
317
318 event = __get_bet_event(bet_event_id)
319 if event is None:
320 return "", 404
321
322 __update_bet_event(bet_event_id, updates={"state": state_enum})
323 db.session.commit()
324 return "success", 200
325 else:
326 print("Error: expected json input to /api/betevent/<bet_event_id>")
327
328
329def datetime_to_utc(dt):
330 return calendar.timegm(dt.utctimetuple())
331
332# get a single bet event
333@app.route('/betevent/<bet_event_id>')
334def bet_event_view(bet_event_id):
335 bet_event = __get_bet_event(bet_event_id)
336 if not bet_event:
337 return "", 404
338 else:
339 user = User.query.get(g.user['id'])
340 bet_event.view_count = bet_event.view_count + 1
341 db.session.commit()
342 return render_template('bet-event-view.html', bet_event=bet_event, user=user, BetEventState=BetEventState, betevent_state=get_betevent_state(bet_event), assignPoints=assignPoints())
343
344def assignPoints():
345 #First we have to get a list of the bet events
346 betEvents = get_bet_events()
347
348 #if len(betEvents) == 0:
349 # print("No bet events to show!")
350
351 #while betEvents is not empty
352 while len(betEvents) != 0:
353 #pop the first bet event
354 event = betEvents.pop(0)
355 #extract the bet_event_id, the list of betters, the total amount on the bet event, and the list of moderators
356 betEventId = event.id
357 betters = event.betters
358 totalAmount = event.totalAmount
359 moderators = event.moderators
360
361 print()
362 print()
363
364 print("The bet id is: " + str(betEventId))
365 print("The list of betters are: " + str(betters))
366 print("The total amount of the event is: " + str(totalAmount))
367 print("The list of moderators are: " + str(moderators))
368 #Completed: determine the option by checking through the list of moderators and seeing what the popular option was (could do ratio, ect)
369 #We need to make moderate work first before doing the step above
370
371 #TODO: Add points to people's acounts for moderating a winning event.
372
373 winningOption = 0
374 zeroCount = 0
375 oneCount = 0
376
377 if len(moderators) != 0:
378 for mod in moderators:
379 if mod.option == 0:
380 zeroCount+=1
381 else:
382 oneCount+=1
383
384 print("The number of moderators that chose option 0 are: " + str(zeroCount))
385 print("The number of moderators that chose option 1 are: " + str(oneCount))
386 if zeroCount > oneCount:
387 payModerators(moderators,0)
388 winningOption = 0
389 elif zeroCount < oneCount:
390 payModerators(moderators,1)
391 winningOption = 1
392 else:
393 #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.
394 winningOption = 0
395
396 winningAmount = 0
397 print("The winning option is: " + str(winningOption))
398 #*BETS
399 for better in betters:
400 if(better.option == winningOption):
401 betterId = better.users_id
402 winningAmount+= better.amount
403
404 print("The better id's are: " + str(betterId))
405 print("The winning amount is: " + str(winningAmount))
406
407 for better in betters:
408 if(better.option == winningOption):
409 betterId = better.users_id
410 payOut = (better.amount / winningAmount) * totalAmount
411
412 print("The payout for: " + str(better.better.name + " is: " + str(payOut)))
413
414 user = User.query.get(betterId)
415 user.points += payOut
416
417 event.hasBeenDistributed = 1
418 db.session.commit()
419
420 print()
421 print()
422
423 #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
424 #Move on to the next event.
425
426 #Distribute points, and shift the bit from 0 to 1
427 return u'success'
428
429def payModerators(moderators, option):
430
431 for moderator in moderators:
432 modUserId = moderator.users_id
433
434 if moderator.option == option:
435 user = User.query.get(modUserId)
436 user.points +=10
437
438 db.session.commit()
439
440@app.before_request
441def get_current_user():
442 """Set g.user to the currently logged in user.
443
444 Called before each request, get_current_user sets the global g.user
445 variable to the currently logged in user. A currently logged in user is
446 determined by seeing if it exists in Flask's session dictionary.
447
448 If it is the first time the user is logging into this application it will
449 create the user and insert it into the database. If the user is not logged
450 in, None will be set to g.user.
451 """
452
453 # Set the user in the session dictionary as a global g.user and bail out
454 # of this function early.
455 if session.get('user'):
456 g.user = session.get('user')
457 return
458
459 # Attempt to get the short term access token for the current user.
460 result = get_user_from_cookie(cookies=request.cookies, app_id=FB_APP_ID,
461 app_secret=FB_APP_SECRET)
462
463 # If there is no result, we assume the user is not logged in.
464 if result:
465 # Check to see if this user is already in our database.
466 user = User.query.filter(User.fb_id == result['uid']).first()
467
468 if not user:
469 # Not an existing user so get info
470 graph = GraphAPI(result['access_token'])
471 profile = graph.get_object('me')
472 if 'link' not in profile:
473 profile['link'] = ""
474
475 # Create the user and insert it into the database
476 user = User(fb_id=str(profile['id']), name=profile['name'],
477 profile_url=profile['link'],
478 access_token=result['access_token'])
479 db.session.add(user)
480 elif user.access_token != result['access_token']:
481 # If an existing user, update the access token
482 user.access_token = result['access_token']
483
484 # Commit changes to the database
485 db.session.commit()
486
487 # Add the user to the current session
488 session['user'] = dict(id=user.id, name=user.name, profile_url=user.profile_url,
489 fb_id=user.fb_id, points=user.points,access_token=user.access_token)
490
491 # set the user as a global g.user
492 g.user = session.get('user', None)