11from flask import Blueprint , jsonify , current_app , request , abort , render_template
2- from flask_jwt_extended import get_current_user , jwt_optional
2+ from flask_limiter . util import get_remote_address
33from flask_mail import Message
44from itsdangerous import URLSafeSerializer
5- from sqlalchemy import or_
5+ from sqlalchemy import or_ , func
66
77from .. import core
8+ from ..limiter import limiter
89from ..auth import Users
910from ..mail import mail
1011from ..models import Answer , db , Vote , Question
@@ -35,33 +36,35 @@ def get_contestants():
3536 try :
3637 page = int (request .args .get ("page" , 1 ))
3738 per = int (request .args .get ("per" , 20 ))
39+ desc = request .args .get ("desc" )
3840 except ValueError :
3941 return jsonify (status = "error" ,
4042 reason = "invalid 'page' or 'per' parameter" ), 400
4143
42- max_rank = core .max_rank ()
43-
44- p = Answer .query \
44+ q = Answer .query .with_entities (
45+ Answer .id ,
46+ Answer .text ,
47+ func .count (Answer .votes ),
48+ Users .studentfirstname ,
49+ Users .studentlastname ,
50+ Users .username ,
51+ func .concat (Users .studentfirstname , func .right (Users .studentlastname , 1 ))
52+ ) \
4553 .join (Answer .question ) \
46- .filter (Question .rank == max_rank ,
47- Answer .correct ) \
48- .paginate (page = page , per_page = per )
54+ .join (Answer .user ) \
55+ .outerjoin (Answer .votes ) \
56+ .filter (Question .rank == core .max_rank ()) \
57+ .group_by (Answer .id )
4958
50- contestants = []
51- for ans in p .items : # type: Answer
59+ if desc is not None :
60+ q = q .order_by (func .count (Answer .votes ).desc ())
61+ else :
62+ q = q .order_by (Answer .id )
5263
53- contestants .append (dict (
54- id = ans .id ,
55- text = ans .text ,
56- numVotes = ans .confirmed_votes (),
57- firstName = ans .user .studentfirstname ,
58- lastName = ans .user .studentlastname ,
59- username = ans .user .username ,
60- display = ans .user .display ()
61- ))
64+ p = q .paginate (page = page , per_page = per )
6265
6366 return jsonify (
64- items = contestants ,
67+ items = p . items ,
6568 totalItems = p .total ,
6669 page = p .page ,
6770 totalPages = p .pages ,
@@ -72,7 +75,21 @@ def get_contestants():
7275 )
7376
7477
78+ def normalize_email (email ):
79+
80+ local , domain = email .rsplit ("@" )
81+
82+ if domain == "gmail.com" :
83+ local = local .replace ("." , "" )
84+
85+ if "+" in local :
86+ local = local .split ("+" )[0 ]
87+
88+ return local + "@" + domain
89+
90+
7591@bp .route ("/<int:answer_id>/cast" , methods = ["POST" ])
92+ @limiter .limit ("4 per day" , key_func = get_remote_address )
7693def vote_cast (answer_id : int ):
7794 """Cast a vote on an Answer"""
7895 max_rank = core .max_rank ()
@@ -93,13 +110,13 @@ def vote_cast(answer_id: int):
93110 v .answer_id = ans .id
94111
95112 try :
96- v .voter_email = request .json ["email" ]
113+ v .voter_email = normalize_email ( request .json ["email" ])
97114 except (TypeError , KeyError ):
98115 return jsonify (status = "error" ,
99116 message = "no student email defined. an 'email' property "
100117 "is required on the JSON body." ), 400
101118
102- if v .voter_email is None :
119+ if v .voter_email is None or v . voter_email == "" :
103120 return jsonify (status = "error" ,
104121 reason = "voter email required" ), 400
105122
0 commit comments