mirror of
https://github.com/DMOJ/online-judge.git
synced 2024-11-25 16:32:37 +08:00
Add HTML/CSS for problem point voting
This commit is contained in:
parent
8f259d1d97
commit
a93756d8a1
91
resources/problem-vote.js
Normal file
91
resources/problem-vote.js
Normal file
@ -0,0 +1,91 @@
|
||||
var voteChart = null;
|
||||
|
||||
function init_problem_vote_form() {
|
||||
$('#delete-problem-vote-form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: $('#delete-problem-vote-form').prop('action'),
|
||||
type: 'POST',
|
||||
data: $('#delete-problem-vote-form').serialize(),
|
||||
success: function () {
|
||||
$('#problem-vote-button').text(gettext('Vote on problem points'));
|
||||
$.featherlight.close();
|
||||
},
|
||||
error: function (data) {
|
||||
var msg = 'responseJSON' in data ? data.responseJSON.message : data.statusText;
|
||||
alert(interpolate(gettext('Unable to delete vote: %s'), [msg]));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#problem-vote-form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: $('#problem-vote-form').prop('action'),
|
||||
type: 'POST',
|
||||
data: $('#problem-vote-form').serialize(),
|
||||
success: function (data) {
|
||||
$('#problem-vote-button').text(interpolate(gettext('Edit points vote (%s)'), [data.points]));
|
||||
$.featherlight.close();
|
||||
},
|
||||
error: function (data) {
|
||||
var errors = 'responseJSON' in data ? data.responseJSON : {'message': data.statusText};
|
||||
if ('message' in errors) {
|
||||
alert(interpolate(gettext('Unable to cast vote: %s'), [errors.message]));
|
||||
}
|
||||
$('#points-error').text('points' in errors ? errors.points[0] : '');
|
||||
$('#note-error').text('note' in errors ? errors.note[0] : '');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reload_problem_vote_graph(data, min_possible_vote, max_possible_vote) {
|
||||
if (voteChart !== null) voteChart.destroy();
|
||||
|
||||
// Give the graph some padding on both sides.
|
||||
var min_points = Math.max(data[0] - 2, min_possible_vote);
|
||||
var max_points = Math.min(data[data.length - 1] + 2, max_possible_vote);
|
||||
|
||||
var xlabels = [];
|
||||
var voteFreq = [];
|
||||
for (var i = min_points; i <= max_points; i++) {
|
||||
xlabels.push(i);
|
||||
voteFreq.push(0);
|
||||
}
|
||||
|
||||
data.forEach(function (x) { voteFreq[x - min_points]++; });
|
||||
var max_number_of_votes = Math.max.apply(null, voteFreq);
|
||||
|
||||
var voteData = {
|
||||
labels: xlabels,
|
||||
datasets: [{
|
||||
label: gettext('Number of votes for this point value'),
|
||||
data: voteFreq,
|
||||
borderColor: 'red',
|
||||
backgroundColor: 'pink',
|
||||
}],
|
||||
};
|
||||
var voteDataConfig = {
|
||||
type: 'bar',
|
||||
data: voteData,
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
precision: 0,
|
||||
suggestedMax: Math.ceil(max_number_of_votes * 1.2),
|
||||
beginAtZero: true,
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: false,
|
||||
}
|
||||
}],
|
||||
},
|
||||
},
|
||||
};
|
||||
voteChart = new Chart($('#problem-vote-chart').get(0), voteDataConfig);
|
||||
}
|
54
resources/problem-vote.scss
Normal file
54
resources/problem-vote.scss
Normal file
@ -0,0 +1,54 @@
|
||||
.problem-vote-container {
|
||||
margin: 1em;
|
||||
min-width: 25em;
|
||||
}
|
||||
|
||||
.problem-vote-form-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.problem-vote-form-title {
|
||||
font-size: 2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.problem-vote-date {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.problem-vote-form-info {
|
||||
font-size: 1.2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
#problem-vote-form textarea {
|
||||
margin-top: 0.5em;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.problem-voting-form-error {
|
||||
font-size: 1.2em;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.problem-vote-submits {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.problem-vote-stats-bar {
|
||||
font-size: 1.2em;
|
||||
font-weight: 500;
|
||||
margin: 0.6em 0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.problem-vote-stats-bar span {
|
||||
margin: 0 0.6em;
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
@import "status";
|
||||
@import "blog";
|
||||
@import "problem";
|
||||
@import "problem-vote";
|
||||
@import "ranks";
|
||||
@import "users";
|
||||
@import "content-description";
|
||||
|
@ -46,6 +46,11 @@
|
||||
|
||||
{% block content_js_media %}
|
||||
{% include "comments/media-js.html" %}
|
||||
|
||||
{% if vote_perm.can_view() %}
|
||||
<script src="{{ static('libs/chart.js/Chart.js') }}" type="text/javascript"></script>
|
||||
<script src="{{ static('problem-vote.js') }}" type="text/javascript"></script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
@ -126,6 +131,21 @@
|
||||
{% endif %}
|
||||
<div><a href="{{ url('chronological_submissions', problem.code) }}">{{ _('All submissions') }}</a></div>
|
||||
<div><a href="{{ url('ranked_submissions', problem.code) }}">{{ _('Best submissions') }}</a></div>
|
||||
|
||||
{% if vote_perm.can_view() %}
|
||||
<hr style="padding-top: 0.1em">
|
||||
{% if vote_perm.can_vote() %}
|
||||
<div><a href="#" data-featherlight="{{ url('problem_vote', problem.code) }}" id="problem-vote-button">
|
||||
{%- if vote is none -%}
|
||||
{{ _('Vote on problem points') }}
|
||||
{%- else -%}
|
||||
{{ _('Edit points vote (%(points)d)', points=vote.points) }}
|
||||
{%- endif -%}
|
||||
</a></div>
|
||||
{% endif %}
|
||||
<div><a href="#" data-featherlight="{{ url('problem_vote_stats', problem.code) }}">{{ _('Voting statistics') }}</a></div>
|
||||
{% endif %}
|
||||
|
||||
{% if (editorial and editorial.is_accessible_by(request.user)) and not request.in_contest %}
|
||||
<hr>
|
||||
<div><a href="{{ url('problem_editorial', problem.code) }}">{{ _('Read editorial') }}</a></div>
|
||||
|
42
templates/problem/vote-ajax.html
Normal file
42
templates/problem/vote-ajax.html
Normal file
@ -0,0 +1,42 @@
|
||||
<div class="problem-vote-container">
|
||||
<div class="problem-vote-form-header">
|
||||
{% if vote %}
|
||||
<span class="problem-vote-form-title">{{ _('Change vote') }}</span>
|
||||
<span class="problem-vote-date">
|
||||
{%- with vote_date=vote.vote_time|date(_('N j, Y, g:i a')) -%}
|
||||
{{ _('Last voted on %(date)s', date=vote_date) }}
|
||||
{%- endwith -%}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="problem-vote-form-title">{{ _('Cast vote') }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<form id="delete-problem-vote-form" action="{{ url('delete_problem_vote', problem.code) }}" method="post">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
<br>
|
||||
<form id="problem-vote-form" action="{{ url('problem_vote', problem.code) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<span class="problem-vote-form-info">{{ _('Points:') }}</span>
|
||||
<input type="number" step="1" placeholder="{{ problem.points|floatformat(0) }}" value="{% if vote %}{{ vote.points }}{% endif %}"
|
||||
min="{{ min_possible_vote }}" max="{{ max_possible_vote }}" name="points" required>
|
||||
<div id="points-error" class="problem-voting-form-error"></div>
|
||||
<br>
|
||||
<span class="problem-vote-form-info">{{ _('Justification:') }}</span>
|
||||
<br>
|
||||
<textarea name="note" rows="12" placeholder="{{ _("A short justification for your vote to this problem's point value (optional).") }}">
|
||||
{%- if vote %}{{ vote.note }}{% endif -%}
|
||||
</textarea>
|
||||
<div id="note-error" class="problem-voting-form-error"></div>
|
||||
<br>
|
||||
<div class="problem-vote-submits">
|
||||
<input type="submit" value="{{ _('Vote!') }}">
|
||||
{% if vote %}
|
||||
<input type="submit" value="{{ _('Delete vote') }}" form="delete-problem-vote-form">
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
init_problem_vote_form();
|
||||
</script>
|
||||
</div>
|
22
templates/problem/vote-stats-ajax.html
Normal file
22
templates/problem/vote-stats-ajax.html
Normal file
@ -0,0 +1,22 @@
|
||||
<div class="problem-vote-container">
|
||||
<div class="problem-vote-form-header">
|
||||
<span class="problem-vote-form-title">{{ _('Voting statistics') }}</span>
|
||||
</div>
|
||||
{% if not votes %}
|
||||
<div class="problem-vote-stats-bar">
|
||||
<span>{{ _('No votes available!') }}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<canvas id="problem-vote-chart"></canvas>
|
||||
<script type="text/javascript">
|
||||
reload_problem_vote_graph({{ votes }}, {{ min_possible_vote }}, {{ max_possible_vote }});
|
||||
</script>
|
||||
<div class="problem-vote-stats-bar">
|
||||
{% with median_str=median|floatformat(1), mean_str=mean|floatformat(1), vote_count=votes|length %}
|
||||
<span>{{ _('Median vote: %(val)s', val=median_str) }}</span>
|
||||
<span>{{ _('Mean vote: %(val)s', val=mean_str) }}</span>
|
||||
<span>{{ _('Number of votes: %(val)d', val=vote_count) }}</span>
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user