From f563cd5a21c725d88c6e4d46babe37eb80138801 Mon Sep 17 00:00:00 2001 From: CodeKevin Date: Sun, 24 Apr 2016 17:30:57 -0400 Subject: [PATCH] Adding Awards feature which closes #84 The Awards feature allows an admin to give users points. Eventually Awards will allow for uploading icons to make the award more commemorative. --- CTFd/admin.py | 23 +++++++++++++-- CTFd/scoreboard.py | 54 +++++++++++++++++++++++---------- CTFd/templates/team.html | 64 ++++++++++++++++++++++++++-------------- CTFd/views.py | 7 +++-- 4 files changed, 105 insertions(+), 43 deletions(-) diff --git a/CTFd/admin.py b/CTFd/admin.py index 2429bda..64fefe6 100644 --- a/CTFd/admin.py +++ b/CTFd/admin.py @@ -3,6 +3,7 @@ from CTFd.utils import sha512, is_safe_url, authed, admins_only, is_admin, unix_ from CTFd.models import db, Teams, Solves, Awards, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config, DatabaseError from itsdangerous import TimedSerializer, BadTimeSignature from sqlalchemy.sql import and_, or_, not_ +from sqlalchemy.sql.expression import union_all from werkzeug.utils import secure_filename from socket import inet_aton, inet_ntoa from passlib.hash import bcrypt_sha256 @@ -493,10 +494,26 @@ def admin_graph(graph_type): @admins_only def admin_scoreboard(): score = db.func.sum(Challenges.value).label('score') - quickest = db.func.max(Solves.date).label('quickest') - teams = db.session.query(Solves.teamid, Teams.name, Teams.banned, score).join(Teams).join(Challenges).group_by(Solves.teamid).order_by(score.desc(), quickest) + scores = db.session.query(Solves.teamid.label('teamid'), Teams.name.label('name'), score, Solves.date.label('date')) \ + .join(Teams) \ + .join(Challenges) \ + .filter(Teams.banned == None) \ + .group_by(Solves.teamid) + + awards = db.session.query(Teams.id.label('teamid'), Teams.name.label('name'), + db.func.sum(Awards.value).label('score'), Awards.date.label('date')) \ + .filter(Teams.id == Awards.teamid) \ + .group_by(Teams.id) + + results = union_all(scores, awards) + + standings = db.session.query(results.columns.teamid, results.columns.name, + db.func.sum(results.columns.score).label('score')) \ + .group_by(results.columns.teamid) \ + .order_by(db.func.sum(results.columns.score).desc(), db.func.max(results.columns.date)) \ + .all() db.session.close() - return render_template('admin/scoreboard.html', teams=teams) + return render_template('admin/scoreboard.html', teams=standings) @admin.route('/admin/teams//awards', methods=['GET']) diff --git a/CTFd/scoreboard.py b/CTFd/scoreboard.py index 121acd4..e07f834 100644 --- a/CTFd/scoreboard.py +++ b/CTFd/scoreboard.py @@ -1,6 +1,7 @@ from flask import current_app as app, session, render_template, jsonify, Blueprint, redirect, url_for, request from CTFd.utils import unix_time, authed, get_config -from CTFd.models import db, Teams, Solves, Challenges +from CTFd.models import db, Teams, Solves, Awards, Challenges +from sqlalchemy.sql.expression import union_all scoreboard = Blueprint('scoreboard', __name__) @@ -10,14 +11,26 @@ def scoreboard_view(): if get_config('view_scoreboard_if_authed') and not authed(): return redirect(url_for('auth.login', next=request.path)) score = db.func.sum(Challenges.value).label('score') - quickest = db.func.max(Solves.date).label('quickest') - teams = db.session.query(Solves.teamid, Teams.name, score)\ - .join(Teams)\ - .join(Challenges)\ - .filter(Teams.banned == None)\ - .group_by(Solves.teamid).order_by(score.desc(), quickest) + scores = db.session.query(Solves.teamid.label('teamid'), Teams.name.label('name'), score, Solves.date.label('date')) \ + .join(Teams) \ + .join(Challenges) \ + .filter(Teams.banned == None) \ + .group_by(Solves.teamid) + + awards = db.session.query(Teams.id.label('teamid'), Teams.name.label('name'), + db.func.sum(Awards.value).label('score'), Awards.date.label('date')) \ + .filter(Teams.id == Awards.teamid) \ + .group_by(Teams.id) + + results = union_all(scores, awards) + + standings = db.session.query(results.columns.teamid, results.columns.name, + db.func.sum(results.columns.score).label('score')) \ + .group_by(results.columns.teamid) \ + .order_by(db.func.sum(results.columns.score).desc(), db.func.max(results.columns.date)) \ + .all() db.session.close() - return render_template('scoreboard.html', teams=teams) + return render_template('scoreboard.html', teams=standings) @scoreboard.route('/scores') @@ -25,15 +38,26 @@ def scores(): if get_config('view_scoreboard_if_authed') and not authed(): return redirect(url_for('auth.login', next=request.path)) score = db.func.sum(Challenges.value).label('score') - quickest = db.func.max(Solves.date).label('quickest') - teams = db.session.query(Solves.teamid, Teams.name, score)\ - .join(Teams)\ - .join(Challenges)\ - .filter(Teams.banned == None)\ - .group_by(Solves.teamid).order_by(score.desc(), quickest) + scores = db.session.query(Solves.teamid.label('teamid'), Teams.name.label('name'), score, Solves.date.label('date')) \ + .join(Teams) \ + .join(Challenges) \ + .filter(Teams.banned == None) \ + .group_by(Solves.teamid) + + awards = db.session.query(Teams.id.label('teamid'), Teams.name.label('name'), db.func.sum(Awards.value).label('score'), Awards.date.label('date'))\ + .filter(Teams.id==Awards.teamid)\ + .group_by(Teams.id) + + results = union_all(scores, awards) + + standings = db.session.query(results.columns.teamid, results.columns.name, db.func.sum(results.columns.score).label('score'))\ + .group_by(results.columns.teamid)\ + .order_by(db.func.sum(results.columns.score).desc(), db.func.max(results.columns.date))\ + .all() + db.session.close() json = {'standings':[]} - for i, x in enumerate(teams): + for i, x in enumerate(standings): json['standings'].append({'pos':i+1, 'id':x.teamid, 'team':x.name,'score':int(x.score)}) return jsonify(json) diff --git a/CTFd/templates/team.html b/CTFd/templates/team.html index cc295ad..bca5f0f 100644 --- a/CTFd/templates/team.html +++ b/CTFd/templates/team.html @@ -30,31 +30,51 @@
+ {% if awards %}
- {% for award in awards %} -
.col-xs-3
- {% endfor %} +

Awards

+ {% set count = awards|length %} + {% set rem = awards|length % 4 %} + {% for award in awards %} + {% if count >= 4 %} +
+ {% else %} +
+ {% endif %} +

{{ award.name }}

+ {% if award.category %}

{{ award.category }}

{% endif %} +

{{ award.description }}

+

{{ award.value }}

+
+ {% set count = count - 1 %} + {% endfor %}
- - - - - - - - - - - {% for solve in solves %} - - - - - - {% endfor %} - -
ChallengeValueTime
{{ solve.chal.name }}{{ solve.chal.value }}
+
+ {% endif %} + +
+

Solves

+ + + + + + + + + + + {% for solve in solves %} + + + + + + {% endfor %} + +
ChallengeValueTime
{{ solve.chal.name }}{{ solve.chal.value }}
+
{% endblock %} diff --git a/CTFd/views.py b/CTFd/views.py index 01fb12f..402ac0b 100644 --- a/CTFd/views.py +++ b/CTFd/views.py @@ -1,6 +1,6 @@ from flask import current_app as app, render_template, render_template_string, request, redirect, abort, jsonify, json as json_mod, url_for, session, Blueprint, Response from CTFd.utils import authed, ip2long, long2ip, is_setup, validate_url, get_config, set_config, sha512, get_ip -from CTFd.models import db, Teams, Solves, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config +from CTFd.models import db, Teams, Solves, Awards, Challenges, WrongKeys, Keys, Tags, Files, Tracking, Pages, Config from jinja2.exceptions import TemplateNotFound from passlib.hash import bcrypt_sha256 @@ -136,13 +136,14 @@ def team(teamid): if get_config('view_scoreboard_if_authed') and not authed(): return redirect(url_for('auth.login', next=request.path)) user = Teams.query.filter_by(id=teamid).first() - solves = Solves.query.filter_by(teamid=teamid).all() + solves = Solves.query.filter_by(teamid=teamid) + awards = Awards.query.filter_by(teamid=teamid).all() score = user.score() place = user.place() db.session.close() if request.method == 'GET': - return render_template('team.html', solves=solves, team=user, score=score, place=place) + return render_template('team.html', solves=solves, awards=awards, team=user, score=score, place=place) elif request.method == 'POST': json = {'solves':[]} for x in solves: