mirror of https://github.com/JohnHammond/CTFd.git
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
parent
253db6eca8
commit
f563cd5a21
|
@ -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'])
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue