From ae52d4bb1ee888010e3f0f9a02133f05902abf4c Mon Sep 17 00:00:00 2001 From: lanjelot Date: Wed, 17 Mar 2021 07:29:11 +1100 Subject: [PATCH] Add ranking history --- ctfpad/models.py | 25 +++++++++++++---- ctfpad/templates/ctfpad/stats/rank.html | 37 +++++++++++++++++++++---- ctfpad/views/__init__.py | 1 + 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/ctfpad/models.py b/ctfpad/models.py index dc591f9..4654257 100644 --- a/ctfpad/models.py +++ b/ctfpad/models.py @@ -4,7 +4,7 @@ import hashlib from datetime import date, datetime, timedelta from pathlib import Path -from collections import namedtuple +from collections import namedtuple, defaultdict from statistics import mean import zipfile import requests @@ -463,7 +463,7 @@ def jitsi_url(self): def save(self): if self.flag_tracker.has_changed("flag"): - self.status = "solved" + self.status = "solved" if self.flag else "unsolved" self.solvers.add( self.last_update_by ) super(Challenge, self).save() @@ -573,10 +573,7 @@ def last_year_stats(self) -> dict: def get_ranking(self) -> list: - """[summary] - - Returns: - list: [description] + """Return the all time player ranking """ members = Member.objects.filter(solved_challenges__isnull=False).distinct() return sorted( @@ -585,6 +582,22 @@ def get_ranking(self) -> list: reverse=True ) + def get_ranking_history(self) -> list: + """Return the top scoring players for the latest CTFs + """ + stats = [] + for ctf in [c for c in Ctf.objects.filter(visibility='public').order_by('-start_date') if c.is_finished and c.scored_points > 0][:15]: + members = defaultdict(lambda: 0) + for challenge in ctf.solved_challenges: + for member in challenge.solvers.all(): + members[member] += challenge.points / challenge.solvers.count() + + ranked = [''] * 4 + for i, (member, points) in enumerate(sorted(members.items(), key=lambda x: x[1], reverse=True)[:4]): + ranked[i] = '{} - {}%'.format(member.username, int(100 * points / ctf.scored_points)) + + stats.append((ctf.name, ranked)) + return stats SearchResult = namedtuple("SearchResult", "category name description link" ) diff --git a/ctfpad/templates/ctfpad/stats/rank.html b/ctfpad/templates/ctfpad/stats/rank.html index b94a663..4cad6b3 100644 --- a/ctfpad/templates/ctfpad/stats/rank.html +++ b/ctfpad/templates/ctfpad/stats/rank.html @@ -7,11 +7,11 @@
Podium
-
+
{% if ranked_members.1.avatar %} - 1st + 2nd {% else %} 2nd {% endif %} @@ -37,7 +37,7 @@
Podium
{% if ranked_members.2.avatar %} - 1st + 3rd {% else %} 3rd {% endif %} @@ -50,9 +50,6 @@
Podium
-
- -
Ranking
@@ -77,4 +74,32 @@
Ranking
+ +
+ +
+
Last CTFs
+ + + + + + + + + + + + {% for ctf, ranked in ranked_history %} + + + {% for score in ranked %} + + {% endfor %} + + {% endfor %} + +
1st2nd3rd4th
{{ ctf }}{{ score }}
+
+
diff --git a/ctfpad/views/__init__.py b/ctfpad/views/__init__.py index b3c3e7f..b4793fe 100644 --- a/ctfpad/views/__init__.py +++ b/ctfpad/views/__init__.py @@ -97,6 +97,7 @@ def generate_stats(request: HttpRequest) -> HttpResponse: "most_solved": stats.solved_categories(), "last_year_stats": stats.last_year_stats(), "ranked_members": stats.get_ranking(), + "ranked_history": stats.get_ranking_history() } return render(request, "ctfpad/stats/detail.html", context)