Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add ranking history #36

Merged
merged 1 commit into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions ctfpad/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was meant to go with previous PR sorry. With this we can now un-solve a challenge.

self.solvers.add( self.last_update_by )

super(Challenge, self).save()
Expand Down Expand Up @@ -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(
Expand All @@ -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" )
Expand Down
37 changes: 31 additions & 6 deletions ctfpad/templates/ctfpad/stats/rank.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<div id="podium">
<div class="card card-body">
<h5>Podium</h5>
<div id="podium-box" class="row" style="height: 300px">
<div id="podium-box" class="row" style="height: 250px">
<div class="col-md-4 step-container m-0 p-0">
<div>
{% if ranked_members.1.avatar %}
<img width="100px" height="100px" src="{{ranked_members.1.avatar.url}}" alt="1st" class="rounded-circle">
<img width="100px" height="100px" src="{{ranked_members.1.avatar.url}}" alt="2nd" class="rounded-circle">
{% else %}
<img width="100px" height="100px" src="{% static '/images/blank-avatar.png' %}" alt="2nd" class="rounded-circle">
{% endif %}
Expand All @@ -37,7 +37,7 @@ <h5>Podium</h5>
<div class="col-md-4 step-container m-0 p-0">
<div>
{% if ranked_members.2.avatar %}
<img width="100px" height="100px" src="{{ranked_members.2.avatar.url}}" alt="1st" class="rounded-circle">
<img width="100px" height="100px" src="{{ranked_members.2.avatar.url}}" alt="3rd" class="rounded-circle">
{% else %}
<img width="100px" height="100px" src="{% static '/images/blank-avatar.png' %}" alt="3rd" class="rounded-circle">
{% endif %}
Expand All @@ -50,9 +50,6 @@ <h5>Podium</h5>
</div>
</div>
</div>
</div>

<div class="col-sm">
<div class="card card-body">
<h5>Ranking</h5>
<table class="table table-hover">
Expand All @@ -77,4 +74,32 @@ <h5>Ranking</h5>
</table>
</div>
</div>

<div class="col-sm">

<div class="card card-body">
<h5>Last CTFs</h5>
<table class="table table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">1st</th>
<th scope="col">2nd</th>
<th scope="col">3rd</th>
<th scope="col">4th</th>
</tr>
</thead>
<tbody>
{% for ctf, ranked in ranked_history %}
<tr>
<th scope="row">{{ ctf }}</th>
{% for score in ranked %}
<td>{{ score }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
1 change: 1 addition & 0 deletions ctfpad/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down