diff --git a/server/api/decorators.py b/server/api/decorators.py index aba964f..9474ef1 100644 --- a/server/api/decorators.py +++ b/server/api/decorators.py @@ -48,8 +48,6 @@ def api_wrapper(f): return response return wrapper -import user # Must go below api_wrapper to prevent import loops - def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): @@ -58,6 +56,8 @@ def login_required(f): return f(*args, **kwargs) return decorated_function +import user # Must go below api_wrapper to prevent import loops + def admins_only(f): @wraps(f) def decorated_function(*args, **kwargs): diff --git a/server/api/models.py b/server/api/models.py index 3dcd683..8fa9982 100644 --- a/server/api/models.py +++ b/server/api/models.py @@ -1,5 +1,7 @@ from flask.ext.sqlalchemy import SQLAlchemy + import time +import traceback import utils db = SQLAlchemy() @@ -33,11 +35,14 @@ class Teams(db.Model): teamname_lower = db.Column(db.String(64), unique=True) school = db.Column(db.Text) owner = db.Column(db.Integer) + observer = db.Column(db.Boolean) - def __init__(self, teamname, owner): + def __init__(self, teamname, school, owner, observer): self.teamname = teamname self.teamname_lower = teamname.lower() + self.school = school self.owner = owner + self.observer = observer def get_members(self): members = [ ] @@ -47,6 +52,25 @@ class Teams(db.Model): }) return members + def points(self): + score = db.func.sum(Problems.value).label("score") + team = db.session.query(Solves.tid, score).join(Teams).join(Problems).filter(Teams.tid==self.tid).group_by(Solves.tid).first() + if team: + return team.score + else: + return 0 + + def place(self): + score = db.func.sum(Problems.value).label("score") + quickest = db.func.max(Solves.date).label("quickest") + teams = db.session.query(Solves.tid).join(Teams).join(Problems).filter().group_by(Solves.tid).order_by(score.desc(), quickest).all() + try: + i = teams.index((self.tid,)) + 1 + k = i % 10 + return (i, "%d%s" % (i, "tsnrhtdd"[(i / 10 % 10 != 1) * (k < 4) * k::4])) + except ValueError: + return (-1, "--") + class Problems(db.Model): pid = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) @@ -77,13 +101,19 @@ class Files(db.Model): class Solves(db.Model): sid = db.Column(db.Integer, primary_key=True) - pid = db.Column(db.Integer) - tid = db.Column(db.Integer) + pid = db.Column(db.Integer, db.ForeignKey("problems.pid")) + tid = db.Column(db.Integer, db.ForeignKey("teams.tid")) date = db.Column(db.Integer, default=utils.get_time_since_epoch()) + team = db.relationship("Teams", foreign_keys="Solves.tid", lazy="joined") + prob = db.relationship("Problems", foreign_keys="Solves.pid", lazy="joined") + correct = db.Column(db.Boolean) + flag = db.Column(db.Text) - def __init__(self, pid, tid): + def __init__(self, pid, tid, flag, correct): self.pid = pid self.tid = tid + self.flag = flag + self.correct = correct ########## # TOKENS # diff --git a/server/api/team.py b/server/api/team.py index 02f7d69..0e59427 100644 --- a/server/api/team.py +++ b/server/api/team.py @@ -4,9 +4,9 @@ from voluptuous import Schema, Length, Required from models import db, Teams, Users from decorators import api_wrapper, login_required, WebException -from user import in_team, get_user, is_logged_in from schemas import verify_to_schema, check +import user import utils blueprint = Blueprint("team", __name__) @@ -20,18 +20,19 @@ blueprint = Blueprint("team", __name__) @login_required def team_create(): params = utils.flat_multi(request.form) - user = get_user().first() - if user.tid is not None or user.tid >= 0 or get_team(owner=user.uid).first() is not None: + _user = user.get_user().first() + if _user.tid is not None or _user.tid >= 0 or get_team(owner=_user.uid).first() is not None: raise WebException("You're already in a team!") verify_to_schema(TeamSchema, params) teamname = params.get("teamname") + school = params.get("school") - team = Teams(teamname, user.uid) + team = Teams(teamname, school, _user.uid, _user.utype != 1) with app.app_context(): db.session.add(team) db.session.commit() - Users.query.filter_by(uid=user.uid).update({ "tid": team.tid }) + Users.query.filter_by(uid=_user.uid).update({ "tid": team.tid }) db.session.commit() return { "success": 1, "message": "Success!" } @@ -39,8 +40,8 @@ def team_create(): @blueprint.route("/info", methods=["GET"]) @api_wrapper def team_info(): - logged_in = is_logged_in() - me = False + logged_in = user.is_logged_in() + me = False teamname = utils.flat_multi(request.args).get("teamname") if logged_in: my_team = get_team().first() @@ -57,7 +58,8 @@ def team_info(): raise WebException("Team not found.") teamdata = { - "teamname": team.teamname + "teamname": team.teamname, + "school": team.school } teamdata["in_team"] = me return { "success": 1, "team": teamdata } @@ -74,8 +76,25 @@ TeamSchema = Schema({ ([utils.__check_ascii], "Please only use ASCII characters in your teamname."), ([__check_teamname], "This teamname is taken, did you forget your password?") ), + Required("school"): check( + ([str, Length(min=4, max=60)], "Your school name should be between 4 and 60 characters long."), + ([utils.__check_ascii], "Please only use ASCII characters in your school name."), + ), }, extra=True) +def get_team_info(tid=None, teamname=None, teamname_lower=None, owner=None): + team = get_team(tid=tid, teamname=teamname, teamname_lower=teamname_lower, owner=owner).first() + place_number, place = team.place() + result = { + "tid": team.tid, + "teamname": team.teamname, + "school": team.school, + "place": place, + "place_number": place_number, + "points": team.points() + } + return result + def get_team(tid=None, teamname=None, teamname_lower=None, owner=None): match = {} if teamname != None: @@ -86,10 +105,10 @@ def get_team(tid=None, teamname=None, teamname_lower=None, owner=None): match.update({ "tid": tid }) elif owner != None: match.update({ "owner": owner }) - elif is_logged_in(): - user = get_user().first() - if user.tid is not None: - match.update({ "tid": user.tid }) + elif user.is_logged_in(): + _user = user.get_user().first() + if _user.tid is not None: + match.update({ "tid": _user.tid }) with app.app_context(): result = Teams.query.filter_by(**match) return result \ No newline at end of file diff --git a/server/api/user.py b/server/api/user.py index 5c2655b..cf9b7dd 100644 --- a/server/api/user.py +++ b/server/api/user.py @@ -10,6 +10,7 @@ import datetime import logger import re import requests +import team import utils ############### @@ -97,9 +98,10 @@ def user_info(): me = False if not("username" in session) else username.lower() == session["username"].lower() user = get_user(username_lower=username.lower()).first() if user is None: - raise WebException("User not found.") + raise WebException("User not found.") show_email = me if logged_in else False + user_in_team = in_team(user) userdata = { "user_found": True, "name": user.name, @@ -108,10 +110,13 @@ def user_info(): "admin": user.admin, "registertime": datetime.datetime.fromtimestamp(user.registertime).isoformat() + "Z", "me": me, - "show_email": show_email + "show_email": show_email, + "in_team": user_in_team } if show_email: userdata["email"] = user.email + if user_in_team: + userdata["team"] = team.get_team_info(tid=user.tid) return { "success": 1, "user": userdata } ################## diff --git a/web/pages/profile.html b/web/pages/profile.html index e4316c7..a812708 100644 --- a/web/pages/profile.html +++ b/web/pages/profile.html @@ -3,7 +3,7 @@
- + Edit Picture

{{ user.name }}

@{{ user.username }} @@ -38,13 +38,29 @@

Team Information

-
-
-
-
-

{{ user['me']==true ? "You're" : "This user is" }} not a part of a team.

- Join or create one now » -
+
+ + + + + + + + + + + + + + + + + +
Team Name{{ user.team['teamname'] }}
School{{ user.team['school'] }}
Points{{ user.team['points'] }}
Rank{{ user.team['place'] }}
+
+
+

{{ user['me']==true ? "You're" : "This user is" }} not a part of a team.

+ Join or create one now »
diff --git a/web/pages/team.html b/web/pages/team.html index 4f8002b..c0fc96b 100644 --- a/web/pages/team.html +++ b/web/pages/team.html @@ -45,13 +45,44 @@
-
-
- - - - - +
+
+
+

Create a Team

+
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+ + +
+
+
+
+
+