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.
selenium-screenshot-testing
CodeKevin 2016-04-24 17:30:57 -04:00
parent 253db6eca8
commit f563cd5a21
4 changed files with 105 additions and 43 deletions

View File

@ -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/<teamid>/awards', methods=['GET'])

View File

@ -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)

View File

@ -30,31 +30,51 @@
<div class="clearfix"></div>
{% if awards %}
<div class="row">
{% for award in awards %}
<div class="col-xs-3 col-md-3">.col-xs-3</div>
{% endfor %}
<h3>Awards</h3>
{% set count = awards|length %}
{% set rem = awards|length % 4 %}
{% for award in awards %}
{% if count >= 4 %}
<div class="col-xs-3 col-md-3">
{% else %}
<div class="col-xs-{{ (12//rem)|string }} col-md-{{ (12//rem)|string }}">
{% endif %}
<p class="text-center"><strong>{{ award.name }}</strong></p>
{% if award.category %}<p class="text-center">{{ award.category }}</p>{% endif %}
<p class="text-center">{{ award.description }}</p>
<p class="text-center">{{ award.value }}</p>
</div>
{% set count = count - 1 %}
{% endfor %}
</div>
<table class="table table-striped">
<thead>
<tr>
<td><b>Challenge</b></td>
<td class="hidden-xs"><b>Category</b></td>
<td><b>Value</b></td>
<td><b>Time</b></td>
</tr>
</thead>
<tbody>
{% for solve in solves %}
<tr>
<td><a href="/challenges#{{ solve.chal.name }}">{{ solve.chal.name }}</a></td>
<td class="hidden-xs">{{ solve.chal.category }}</td><td>{{ solve.chal.value }}</td>
<td class="solve-time"><script>document.write( moment({{ solve.date|unix_time_millis }}).local().format('MMMM Do, h:mm:ss A'))</script></td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
{% endif %}
<div class="row">
<h3>Solves</h3>
<table class="table table-striped">
<thead>
<tr>
<td><b>Challenge</b></td>
<td class="hidden-xs"><b>Category</b></td>
<td><b>Value</b></td>
<td><b>Time</b></td>
</tr>
</thead>
<tbody>
{% for solve in solves %}
<tr>
<td><a href="/challenges#{{ solve.chal.name }}">{{ solve.chal.name }}</a></td>
<td class="hidden-xs">{{ solve.chal.category }}</td><td>{{ solve.chal.value }}</td>
<td class="solve-time"><script>document.write( moment({{ solve.date|unix_time_millis }}).local().format('MMMM Do, h:mm:ss A'))</script></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="score-graph"></div>
</div>
{% endblock %}

View File

@ -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: