Add problem point distribution
This commit is contained in:
parent
8ac611f9f4
commit
04a20626b8
4 changed files with 87 additions and 2 deletions
|
@ -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)),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
from calendar import Calendar, SUNDAY
|
from calendar import Calendar, SUNDAY
|
||||||
from collections import defaultdict, namedtuple
|
from collections import defaultdict, namedtuple
|
||||||
from datetime import date, datetime, time, timedelta
|
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.opengraph import generate_opengraph
|
||||||
from judge.utils.problems import _get_result_data
|
from judge.utils.problems import _get_result_data
|
||||||
from judge.utils.ranker import ranker
|
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
|
from judge.utils.views import DiggPaginatorMixin, SingleObjectFormView, TitleMixin, generic_message
|
||||||
|
|
||||||
__all__ = ['ContestList', 'ContestDetail', 'ContestRanking', 'ContestJoin', 'ContestLeave', 'ContestCalendar',
|
__all__ = ['ContestList', 'ContestDetail', 'ContestRanking', 'ContestJoin', 'ContestLeave', 'ContestCalendar',
|
||||||
|
@ -524,6 +525,7 @@ class CachedContestCalendar(ContestCalendar):
|
||||||
|
|
||||||
class ContestStats(TitleMixin, ContestMixin, DetailView):
|
class ContestStats(TitleMixin, ContestMixin, DetailView):
|
||||||
template_name = 'contest/stats.html'
|
template_name = 'contest/stats.html'
|
||||||
|
POINT_BIN = 10 # in point distribution
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
return _('%s Statistics') % self.object.name
|
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']:
|
for category in _get_result_data(defaultdict(int, status_counts[i]))['categories']:
|
||||||
result_data[category['code']][i] = category['count']
|
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 = {
|
stats = {
|
||||||
'problem_status_count': {
|
'problem_status_count': {
|
||||||
'labels': labels,
|
'labels': labels,
|
||||||
|
@ -574,6 +591,9 @@ class ContestStats(TitleMixin, ContestMixin, DetailView):
|
||||||
queryset.values('contest__problem__order', 'problem__name').annotate(ac_rate=ac_rate)
|
queryset.values('contest__problem__order', 'problem__name').annotate(ac_rate=ac_rate)
|
||||||
.order_by('contest__problem__order').values_list('problem__name', '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(
|
'language_count': get_pie_chart(
|
||||||
queryset.values('language__name').annotate(count=Count('language__name'))
|
queryset.values('language__name').annotate(count=Count('language__name'))
|
||||||
.filter(count__gt=0).order_by('-count').values_list('language__name', 'count'),
|
.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['stats'] = mark_safe(json.dumps(stats))
|
||||||
|
context['problems'] = labels
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,22 @@
|
||||||
$(function () {
|
$(function () {
|
||||||
draw_stacked_bar_chart(window.stats.problem_status_count, $('#problem-status-count'));
|
draw_stacked_bar_chart(window.stats.problem_status_count, $('#problem-status-count'));
|
||||||
draw_bar_chart(window.stats.problem_ac_rate, $('#problem-ac-rate'));
|
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_pie_chart(window.stats.language_count, $('#language-count'));
|
||||||
draw_bar_chart(window.stats.language_ac_rate, $('#language-ac-rate'));
|
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',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{% include "contest/media-js.html" %}
|
{% include "contest/media-js.html" %}
|
||||||
|
@ -44,6 +57,21 @@
|
||||||
<div id="problem-ac-rate" class="chart">
|
<div id="problem-ac-rate" class="chart">
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
{{ _('Problem Point Distribution') }}:
|
||||||
|
<select id="problem-point-select">
|
||||||
|
{% for name in problems %}
|
||||||
|
<option value="{{ loop.index0 }}" class="point-dropdown">
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div id="problem-point" class="chart">
|
||||||
|
<canvas></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>{{ _('Submissions by Language') }}</h3>
|
<h3>{{ _('Submissions by Language') }}</h3>
|
||||||
<div id="language-count" class="chart">
|
<div id="language-count" class="chart">
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue