diff --git a/CTFd/admin.py b/CTFd/admin.py index 64fefe6f..c6b59c81 100644 --- a/CTFd/admin.py +++ b/CTFd/admin.py @@ -587,10 +587,29 @@ def admin_solves(teamid="all"): solves = Solves.query.all() else: solves = Solves.query.filter_by(teamid=teamid).all() + awards = Awards.query.filter_by(teamid=teamid).all() db.session.close() json_data = {'solves':[]} for x in solves: - json_data['solves'].append({'id':x.id, 'chal':x.chal.name, 'chalid':x.chalid,'team':x.teamid, 'value': x.chal.value, 'category':x.chal.category, 'time':unix_time(x.date)}) + json_data['solves'].append({ + 'id': x.id, + 'chal': x.chal.name, + 'chalid': x.chalid, + 'team': x.teamid, + 'value': x.chal.value, + 'category': x.chal.category, + 'time': unix_time(x.date) + }) + for award in awards: + json_data['solves'].append({ + 'chal': award.name, + 'chalid': None, + 'team': award.teamid, + 'value': award.value, + 'category': award.category, + 'time': unix_time(award.date) + }) + json_data['solves'].sort(key=lambda k:k['time']) return jsonify(json_data) diff --git a/CTFd/challenges.py b/CTFd/challenges.py index bea8c379..8fbeef2e 100644 --- a/CTFd/challenges.py +++ b/CTFd/challenges.py @@ -1,7 +1,7 @@ from flask import current_app as app, render_template, request, redirect, abort, jsonify, json as json_mod, url_for, session, Blueprint from CTFd.utils import ctftime, view_after_ctf, authed, unix_time, get_kpm, can_view_challenges, is_admin, get_config, get_ip, is_verified -from CTFd.models import db, Challenges, Files, Solves, WrongKeys, Keys, Tags, Teams +from CTFd.models import db, Challenges, Files, Solves, WrongKeys, Keys, Tags, Teams, Awards from sqlalchemy.sql import and_, or_, not_ @@ -76,10 +76,28 @@ def solves(teamid=None): return redirect(url_for('auth.login', next='solves')) else: solves = Solves.query.filter_by(teamid=teamid).all() + awards = Awards.query.filter_by(teamid=teamid).all() db.session.close() json = {'solves':[]} - for x in solves: - json['solves'].append({ 'chal':x.chal.name, 'chalid':x.chalid,'team':x.teamid, 'value': x.chal.value, 'category':x.chal.category, 'time':unix_time(x.date)}) + for solve in solves: + json['solves'].append({ + 'chal': solve.chal.name, + 'chalid': solve.chalid, + 'team': solve.teamid, + 'value': solve.chal.value, + 'category': solve.chal.category, + 'time': unix_time(solve.date) + }) + for award in awards: + json['solves'].append({ + 'chal': award.name, + 'chalid': None, + 'team': award.teamid, + 'value': award.value, + 'category': award.category, + 'time': unix_time(award.date) + }) + json['solves'].sort(key=lambda k: k['time']) return jsonify(json) diff --git a/CTFd/scoreboard.py b/CTFd/scoreboard.py index e07f834e..3a5d5c94 100644 --- a/CTFd/scoreboard.py +++ b/CTFd/scoreboard.py @@ -76,24 +76,43 @@ def topteams(count): json = {'scores':{}} 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)\ - .limit(count) + 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) - for team in teams: + 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)) \ + .limit(count).all() + + for team in standings: solves = Solves.query.filter_by(teamid=team.teamid).all() + awards = Awards.query.filter_by(teamid=team.teamid).all() json['scores'][team.name] = [] + scores = [] for x in solves: json['scores'][team.name].append({ - 'id': x.teamid, 'chal': x.chalid, 'team': x.teamid, 'value': x.chal.value, 'time': unix_time(x.date) }) - + for award in awards: + json['scores'][team.name].append({ + 'chal': None, + 'team': award.teamid, + 'value': award.value, + 'time': unix_time(award.date) + }) + json['scores'][team.name] = sorted(json['scores'][team.name], key=lambda k: k['time']) return jsonify(json) diff --git a/CTFd/static/admin/js/team.js b/CTFd/static/admin/js/team.js index 86a6216f..cc8bfda1 100644 --- a/CTFd/static/admin/js/team.js +++ b/CTFd/static/admin/js/team.js @@ -37,7 +37,12 @@ function scoregraph () { type: 'scatter' } ]; - Plotly.newPlot('score-graph', data); + + var layout = { + title: 'Score over Time' + }; + + Plotly.newPlot('score-graph', data, layout); }); } @@ -62,8 +67,7 @@ function keys_percentage_graph(){ }]; var layout = { - height: 400, - width: 500 + title: 'Key Percentages' }; Plotly.newPlot('keys-pie-graph', data, layout); @@ -106,8 +110,7 @@ function category_breakdown_graph(){ }]; var layout = { - height: 400, - width: 500 + title:'Category Breakdown' }; Plotly.newPlot('categories-pie-graph', data, layout); @@ -116,5 +119,11 @@ function category_breakdown_graph(){ category_breakdown_graph(); keys_percentage_graph(); -adjust_times(); scoregraph(); + + +window.onresize = function () { + Plotly.Plots.resize(document.getElementById('keys-pie-graph')); + Plotly.Plots.resize(document.getElementById('categories-pie-graph')); + Plotly.Plots.resize(document.getElementById('score-graph')); +}; \ No newline at end of file diff --git a/CTFd/static/css/style.css b/CTFd/static/css/style.css index a4946658..b85cfe4d 100644 --- a/CTFd/static/css/style.css +++ b/CTFd/static/css/style.css @@ -101,6 +101,9 @@ table{ } #score-graph{ + height: 450px; + display: block; + clear: both; } #keys-pie-graph{ diff --git a/CTFd/static/js/scoreboard.js b/CTFd/static/js/scoreboard.js index d4f96a08..e062f5ac 100644 --- a/CTFd/static/js/scoreboard.js +++ b/CTFd/static/js/scoreboard.js @@ -63,9 +63,13 @@ function scoregraph () { } function update(){ - updatescores() - scoregraph() + updatescores(); + scoregraph(); } setInterval(update, 300000); // Update scores every 5 minutes -scoregraph() +scoregraph(); + +window.onresize = function () { + Plotly.Plots.resize(document.getElementById('score-graph')); +}; \ No newline at end of file diff --git a/CTFd/static/js/team.js b/CTFd/static/js/team.js index 7bc8468d..07fb28ee 100644 --- a/CTFd/static/js/team.js +++ b/CTFd/static/js/team.js @@ -46,7 +46,12 @@ function scoregraph() { type: 'scatter' } ]; - Plotly.newPlot('score-graph', data); + + var layout = { + title: 'Score over Time' + }; + + Plotly.newPlot('score-graph', data, layout); }); } @@ -70,12 +75,12 @@ function keys_percentage_graph() { type: 'pie' }]; - //var layout = { - // height: 400, - // width: 500 - //}; + var layout = { + title: 'Key Percentages' + }; - Plotly.newPlot('keys-pie-graph', data); + + Plotly.newPlot('keys-pie-graph', data, layout); }); } @@ -114,15 +119,21 @@ function category_breakdown_graph() { type: 'pie' }]; - //var layout = { - // height: 400, - // width: 500 - //}; + var layout = { + title: 'Category Breakdown' + }; - Plotly.newPlot('categories-pie-graph', data); + Plotly.newPlot('categories-pie-graph', data, layout); }); } -category_breakdown_graph() -keys_percentage_graph() -scoregraph() +category_breakdown_graph(); +keys_percentage_graph(); +scoregraph(); + + +window.onresize = function () { + Plotly.Plots.resize(document.getElementById('keys-pie-graph')); + Plotly.Plots.resize(document.getElementById('categories-pie-graph')); + Plotly.Plots.resize(document.getElementById('score-graph')); +}; \ No newline at end of file diff --git a/CTFd/templates/admin/graphs.html b/CTFd/templates/admin/graphs.html index 1a4ccb3f..1ab040d6 100644 --- a/CTFd/templates/admin/graphs.html +++ b/CTFd/templates/admin/graphs.html @@ -2,15 +2,13 @@ {% block content %}