From 04a20626b82b77475fcbc5d8355468381342cf9d Mon Sep 17 00:00:00 2001 From: cuom1999 Date: Tue, 1 Jun 2021 19:20:39 -0500 Subject: [PATCH] Add problem point distribution --- judge/utils/stats.py | 15 +++++++++++++++ judge/views/contests.py | 24 ++++++++++++++++++++++-- templates/contest/stats.html | 28 ++++++++++++++++++++++++++++ templates/stats/media-js.html | 22 ++++++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/judge/utils/stats.py b/judge/utils/stats.py index cf3b251..ba923aa 100644 --- a/judge/utils/stats.py +++ b/judge/utils/stats.py @@ -51,3 +51,18 @@ def get_bar_chart(data, **kwargs): }, ], } + +def get_histogram(data, **kwargs): + return { + 'labels': [round(i, 1) for i in list(map(itemgetter(0), data))], + 'datasets': [ + { + 'backgroundColor': kwargs.get('fillColor', 'rgba(151,187,205,0.5)'), + 'borderColor': kwargs.get('strokeColor', 'rgba(151,187,205,0.8)'), + 'borderWidth': 1, + 'hoverBackgroundColor': kwargs.get('highlightFill', 'rgba(151,187,205,0.75)'), + 'hoverBorderColor': kwargs.get('highlightStroke', 'rgba(151,187,205,1)'), + 'data': list(map(itemgetter(1), data)), + }, + ], + } \ No newline at end of file diff --git a/judge/views/contests.py b/judge/views/contests.py index c9d1466..5875875 100644 --- a/judge/views/contests.py +++ b/judge/views/contests.py @@ -1,4 +1,5 @@ import json +import math from calendar import Calendar, SUNDAY from collections import defaultdict, namedtuple from datetime import date, datetime, time, timedelta @@ -37,7 +38,7 @@ from judge.utils.celery import redirect_to_task_status from judge.utils.opengraph import generate_opengraph from judge.utils.problems import _get_result_data from judge.utils.ranker import ranker -from judge.utils.stats import get_bar_chart, get_pie_chart +from judge.utils.stats import get_bar_chart, get_pie_chart, get_histogram from judge.utils.views import DiggPaginatorMixin, SingleObjectFormView, TitleMixin, generic_message __all__ = ['ContestList', 'ContestDetail', 'ContestRanking', 'ContestJoin', 'ContestLeave', 'ContestCalendar', @@ -524,6 +525,7 @@ class CachedContestCalendar(ContestCalendar): class ContestStats(TitleMixin, ContestMixin, DetailView): template_name = 'contest/stats.html' + POINT_BIN = 10 # in point distribution def get_title(self): return _('%s Statistics') % self.object.name @@ -558,6 +560,21 @@ class ContestStats(TitleMixin, ContestMixin, DetailView): for category in _get_result_data(defaultdict(int, status_counts[i]))['categories']: result_data[category['code']][i] = category['count'] + problem_points = [[] for _ in range(num_problems)] + point_count = queryset.values('contest__problem__order', 'contest__points')\ + .annotate(count=Count('contest__points')) \ + .filter(points__isnull=False) \ + .order_by('contest__problem__order', 'contest__points') + + counter = [[0 for _ in range(self.POINT_BIN + 1)] for _ in range(num_problems)] + for sub in point_count.iterator(): + problem_idx = sub['contest__problem__order'] - 1 + bin_idx = math.floor(sub['contest__points'] * self.POINT_BIN / 100) + counter[problem_idx][bin_idx] += sub['count'] + for i in range(num_problems): + problem_points[i] = [(j * 100 / self.POINT_BIN, counter[i][j]) + for j in range(len(counter[i]))] + stats = { 'problem_status_count': { 'labels': labels, @@ -574,6 +591,9 @@ class ContestStats(TitleMixin, ContestMixin, DetailView): queryset.values('contest__problem__order', 'problem__name').annotate(ac_rate=ac_rate) .order_by('contest__problem__order').values_list('problem__name', 'ac_rate'), ), + 'problem_point': [get_histogram(problem_points[i]) + for i in range(num_problems) + ], 'language_count': get_pie_chart( queryset.values('language__name').annotate(count=Count('language__name')) .filter(count__gt=0).order_by('-count').values_list('language__name', 'count'), @@ -585,7 +605,7 @@ class ContestStats(TitleMixin, ContestMixin, DetailView): } context['stats'] = mark_safe(json.dumps(stats)) - + context['problems'] = labels return context diff --git a/templates/contest/stats.html b/templates/contest/stats.html index 85a6259..d20b21e 100644 --- a/templates/contest/stats.html +++ b/templates/contest/stats.html @@ -18,9 +18,22 @@ $(function () { draw_stacked_bar_chart(window.stats.problem_status_count, $('#problem-status-count')); draw_bar_chart(window.stats.problem_ac_rate, $('#problem-ac-rate')); + pts_hist = draw_histogram(window.stats.problem_point[0], $('#problem-point')); draw_pie_chart(window.stats.language_count, $('#language-count')); draw_bar_chart(window.stats.language_ac_rate, $('#language-ac-rate')); + + $('#problem-point-select').change(function() { + pts_hist.destroy(); + problem = $(this).val(); + pts_hist = draw_histogram(window.stats.problem_point[problem], + $('#problem-point')); + }) + + $('#problem-point-select').select2({ + width: '10em', + }); }); + {% endcompress %} {% include "contest/media-js.html" %} @@ -44,6 +57,21 @@
+ +

+ {{ _('Problem Point Distribution') }}: + +

+ +
+ +

{{ _('Submissions by Language') }}

diff --git a/templates/stats/media-js.html b/templates/stats/media-js.html index 981ddff..69f9ad8 100644 --- a/templates/stats/media-js.html +++ b/templates/stats/media-js.html @@ -65,4 +65,26 @@ }, }); } + function draw_histogram(data, $chart) { + var ctx = $chart.find('canvas')[0].getContext('2d'); + ctx.canvas.height = 20 * data.labels.length + 100; + var chart = new Chart(ctx, { + type: 'bar', + data: data, + options: { + maintainAspectRatio: false, + legend: { + display: false, + }, + tooltips: { + callbacks: { + label: function(tooltipItem, data) { + return tooltipItem.value; + }, + }, + }, + }, + }); + return chart; + }