From 1bdead45eb87d02ce39ec8137c13d29f949340e5 Mon Sep 17 00:00:00 2001 From: Dan LaManna Date: Wed, 8 Feb 2023 17:08:14 -0500 Subject: [PATCH] Show task submission timespans on the stats page Annotate each task in the challenge stats with its first and last submission dates and render them next to the task name. Unlocked tasks show the range as ending in "Present". --- stade/core/models/challenge.py | 2 ++ stade/core/static/css/stade/dashboard.css | 6 ++++ .../partials/task-submission-timespan.html | 5 +++ stade/core/templates/stats.html | 2 ++ stade/core/tests/test_stats.py | 35 +++++++++++++++++++ 5 files changed, 50 insertions(+) create mode 100644 stade/core/templates/partials/task-submission-timespan.html create mode 100644 stade/core/tests/test_stats.py diff --git a/stade/core/models/challenge.py b/stade/core/models/challenge.py index 562d8945..70497542 100644 --- a/stade/core/models/challenge.py +++ b/stade/core/models/challenge.py @@ -38,6 +38,8 @@ def stats(self): distinct=True, ), num_successful_approaches=models.Count('approach', distinct=True), + first_submission=models.Min('approach__submission__created'), + last_submission=models.Max('approach__submission__created'), ) .filter(challenge=self) .order_by('name') diff --git a/stade/core/static/css/stade/dashboard.css b/stade/core/static/css/stade/dashboard.css index 57c0224b..16025a97 100644 --- a/stade/core/static/css/stade/dashboard.css +++ b/stade/core/static/css/stade/dashboard.css @@ -1,3 +1,9 @@ +.submission-timespan { + font-size: 13px; + font-weight: normal; + margin-left: 0.5em; +} + .main-stats { background: #454545; color: #fff; diff --git a/stade/core/templates/partials/task-submission-timespan.html b/stade/core/templates/partials/task-submission-timespan.html new file mode 100644 index 00000000..7eacd05f --- /dev/null +++ b/stade/core/templates/partials/task-submission-timespan.html @@ -0,0 +1,5 @@ +{% if task.first_submission %} + + ({{ task.first_submission|date:"M Y" }} - {% if task.locked %}{{ task.last_submission|date:"M Y" }}{% else %}Present{% endif %}) + +{% endif %} diff --git a/stade/core/templates/stats.html b/stade/core/templates/stats.html index 40fa2184..938eb9af 100644 --- a/stade/core/templates/stats.html +++ b/stade/core/templates/stats.html @@ -49,6 +49,7 @@

Success/Total

  • {{ task.name }} + {% include "partials/task-submission-timespan.html" %}
    @@ -92,6 +93,7 @@

    Success/Total

  • {{ task.name }} + {% include "partials/task-submission-timespan.html" %}
    diff --git a/stade/core/tests/test_stats.py b/stade/core/tests/test_stats.py new file mode 100644 index 00000000..5fb6351a --- /dev/null +++ b/stade/core/tests/test_stats.py @@ -0,0 +1,35 @@ +from datetime import timedelta + +from django.urls import reverse +from django.utils import timezone +from django.utils.dateformat import format as date_format +import pytest + + +@pytest.mark.django_db +def test_stats_task_submission_timespan(client, task_factory, submission_factory): + locked_task = task_factory(locked=True) + open_task = task_factory(locked=False) + empty_task = task_factory() + + first = submission_factory(approach__task=locked_task) + first.created = timezone.now() - timedelta(days=400) + first.save() + last = submission_factory(approach__task=locked_task) + open_submission = submission_factory(approach__task=open_task) + + stats = {t.id: t for t in locked_task.challenge.stats['tasks']} + assert stats[locked_task.id].first_submission == first.created + assert stats[locked_task.id].last_submission == last.created + + def month(dt): + return date_format(timezone.localtime(dt), 'M Y') + + resp = client.get(reverse('stats')) + assert resp.status_code == 200 + content = resp.content.decode() + + assert f'({month(first.created)} - {month(last.created)})' in content + assert f'({month(open_submission.created)} - Present)' in content + assert content.count('class="submission-timespan"') == 2 + assert empty_task.name in content