diff --git a/frx_challenges/frx_challenges/settings.py b/frx_challenges/frx_challenges/settings.py
index 54a8ccc..b782038 100644
--- a/frx_challenges/frx_challenges/settings.py
+++ b/frx_challenges/frx_challenges/settings.py
@@ -262,8 +262,19 @@
}
EVALUATION_DISPLAY_CONFIG = [
- {"result_key": "chars", "display_name": "Characters"},
- {"result_key": "lines", "display_name": "Lines"},
+ {
+ "result_key": "chars",
+ "display_name": "Characters",
+ # ordering options are "smaller_is_better" and "bigger_is_better"
+ "ordering": "smaller_is_better",
+ "ordering_priority": 2
+ },
+ {
+ "result_key": "lines",
+ "display_name": "Lines",
+ "ordering": "bigger_is_better",
+ "ordering_priority": 1
+ }
]
django_yamlconf.load()
diff --git a/frx_challenges/web/models.py b/frx_challenges/web/models.py
index 27e75f9..0b85f12 100644
--- a/frx_challenges/web/models.py
+++ b/frx_challenges/web/models.py
@@ -6,7 +6,8 @@
from django.contrib.auth.models import User
from django.db import models, transaction
from django_jsonform.models.fields import JSONField
-
+from django.db.models.fields.json import KT
+from typing import Optional, List
# Create your models here.
@@ -40,6 +41,47 @@ class Submission(models.Model):
blank=True, null=True, schema=settings.SITE_SUBMISSION_FORM_SCHEMA
)
+ @property
+ def best_version(self) -> Version:
+ """
+ Return the 'best' evaluated version for this Submission
+ """
+ # Construct a query that returns evaluations that:
+ # 1. Belong to a version that belong to this submission
+ # 2. Have been succesfully evaluated
+ # 3. Have results that contain all the keys we use for ordering
+ # 4. Ordered by the ordering criteria expressed by EVALUATION_DISPLAY_CONFIG
+
+
+ # Sort the display_config by ordering_priority (smaller numbers go first)
+ # This primarily is used for tiebreaking, since we only want the 'best'
+ sorted_display_config = sorted(settings.EVALUATION_DISPLAY_CONFIG, key=lambda dc: -dc["ordering_priority"])
+
+ # Ordering criteria for querying our results
+ ordering_criteria = []
+ # List of keys that *must* be present in a result for it to count
+ result_must_have_keys = []
+
+ for dc in sorted_display_config:
+ result_must_have_keys.append(dc['result_key'])
+ k = KT(f"result__{dc['result_key']}")
+ if dc["ordering"] == "smaller_is_better":
+ k = k.asc()
+ elif dc["ordering"] == "bigger_is_better":
+ k = k.desc()
+ else:
+ raise ValueError(f"Invalid ordering {dc['ordering']} found for result_key {dc['result_key']}")
+ ordering_criteria.append(k)
+
+ best_evaluation = Evaluation.objects.filter(
+ version__submission=self,
+ status=Evaluation.Status.EVALUATED,
+ result__has_keys=result_must_have_keys
+ ).order_by(*ordering_criteria).first()
+
+ if best_evaluation:
+ return best_evaluation.version
+
class Version(models.Model):
"""
@@ -52,7 +94,7 @@ class Status(models.TextChoices):
UPLOADED = "UPLOADED"
CLEARED = "CLEARED"
- submission = models.ForeignKey(Submission, on_delete=models.CASCADE)
+ submission = models.ForeignKey(Submission, on_delete=models.CASCADE, related_name="versions")
date_created = models.DateTimeField(auto_now=True)
# FIXME: Cascade is probably not quite right?
user = models.ForeignKey(User, on_delete=models.CASCADE)
diff --git a/frx_challenges/web/templates/results.html b/frx_challenges/web/templates/results.html
index 0ae3fc6..39261ad 100644
--- a/frx_challenges/web/templates/results.html
+++ b/frx_challenges/web/templates/results.html
@@ -25,65 +25,60 @@
Explanatory Headline
+
+
+
+ ID |
+ Name |
+ Description |
+ Date created |
+ {% for dc in evaluation_display_config %}
+ {{ dc.display_name }} |
+ {% endfor %}
+
+
+
+ {% for result in results %}
+
+ {{ result.submission.id }} |
+
+ {{ result.submission.name }}
+ |
+ {{ result.submission.description }} |
+ {{ result.submission.date_created|date:"c" }} |
+ {% for r in result.best_version.latest_evaluation.ordered_results %}
+
+ {% if r %}
+ {{ r }}
+ {% endif %}
+ |
+ {% endfor %}
+
+ {% endfor %}
+
+
+
+ main();
+
{% endblock body %}
diff --git a/frx_challenges/web/templates/submission/detail.html b/frx_challenges/web/templates/submission/detail.html
index 8315934..a0cb0d9 100644
--- a/frx_challenges/web/templates/submission/detail.html
+++ b/frx_challenges/web/templates/submission/detail.html
@@ -1,11 +1,13 @@
{% extends "page.html" %}
{% block head %}
+ integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
+ crossorigin="anonymous">
+ href="https://cdn.datatables.net/2.0.8/css/dataTables.dataTables.css" />
+
+
{% endblock head %}
{% block body %}
@@ -39,7 +41,7 @@
{{ submission.name }}
{{ v.id }} |
{{ v.filename }} |
- {{ v.date_created|date:"M j Y" }} |
+ {{ v.date_created|date:"c" }} |
{{ v.latest_evaluation.status }} |
{% for r in v.latest_evaluation.ordered_results %}
@@ -61,7 +63,21 @@ {{ submission.name }}
{% endblock body %}
diff --git a/frx_challenges/web/urls.py b/frx_challenges/web/urls.py
index 49a4b89..3c21089 100644
--- a/frx_challenges/web/urls.py
+++ b/frx_challenges/web/urls.py
@@ -4,7 +4,6 @@
urlpatterns = [
path("upload/", default.upload, name="upload"),
- path("results", default.results, name="results"),
path("teams/list", teams.list, name="teams-list"),
path("teams/create", teams.create, name="teams-create"),
path("teams/", teams.view, name="teams-view"),
diff --git a/frx_challenges/web/views/default.py b/frx_challenges/web/views/default.py
index 266864f..f9b83d8 100644
--- a/frx_challenges/web/views/default.py
+++ b/frx_challenges/web/views/default.py
@@ -54,32 +54,40 @@ def upload(request: HttpRequest, id: int) -> HttpResponse:
)
-def results(request: HttpRequest) -> HttpResponse:
- evaluations = Evaluation.objects.all()
-
- evaluations_resp = {
- "display_config": settings.EVALUATION_DISPLAY_CONFIG,
- "results": [],
- }
-
- for ev in evaluations:
- evaluations_resp["results"].append(
- {
- "evaluation_id": ev.id,
- "username": ev.version.user.username,
- "status": ev.status,
- "last_updated": ev.last_updated.isoformat(),
- "result": ev.result,
- }
- )
-
- return JsonResponse(evaluations_resp)
-
-
def leaderboard(request: HttpRequest) -> HttpResponse:
if settings.CHALLENGE_STATE != "RUNNING":
return HttpResponse(
"Challenge hasn't started, so leaderboard is not available", status=400
)
- return render(request, "results.html")
+ sorted_display_config = sorted(settings.EVALUATION_DISPLAY_CONFIG, key=lambda dc: -dc["ordering_priority"])
+ results = []
+ all_submissions = Submission.objects.all()
+ for sub in all_submissions:
+ bv = sub.best_version
+ if not bv:
+ # Only display submissions with at least one 'best version'
+ continue
+ results.append({
+ "submission": sub,
+ "best_version": bv
+ })
+
+ def sort_key_func(r):
+ bv: Version = r["best_version"]
+ sort_key = []
+ for dc in sorted_display_config:
+ if dc["ordering"] == "smaller_is_better":
+ sort_key.append(-bv.latest_evaluation.result[dc["result_key"]])
+ elif dc["ordering"] == "bigger_is_better":
+ sort_key.append(bv.latest_evaluation.result[dc["result_key"]])
+ else:
+ raise ValueError(f"Invalid ordering {dc['ordering']} found for result_key {dc['result_key']}")
+
+ return sort_key
+
+ results = sorted(
+ results,
+ key=sort_key_func
+ )
+ return render(request, "results.html", {"results":results})
diff --git a/frx_challenges/web/views/submissions.py b/frx_challenges/web/views/submissions.py
index fac20df..f770e57 100644
--- a/frx_challenges/web/views/submissions.py
+++ b/frx_challenges/web/views/submissions.py
@@ -60,7 +60,7 @@ def detail(request: HttpRequest, id: int) -> HttpResponse:
submission = queryset.get(id=id)
except Submission.DoesNotExist:
raise Http404("Submission does not exist")
- versions = submission.version_set.all()
+ versions = submission.versions.all()
return render(
request,
"submission/detail.html",
|