Change UI ranking
This commit is contained in:
parent
d38342ad43
commit
9b1724cdad
16 changed files with 291 additions and 631 deletions
|
@ -1,4 +1,142 @@
|
|||
<script src="//cdn.jsdelivr.net/npm/featherlight@1.7.14/release/featherlight.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script type="text/javascript">
|
||||
function isFaster(time1, time2) {
|
||||
let arr1 = time1.split(':');
|
||||
let arr2 = time2.split(':');
|
||||
|
||||
for (let i in arr1) {
|
||||
let val1 = parseInt(arr1[i]);
|
||||
let val2 = parseInt(arr2[i]);
|
||||
if (val1 < val2) return true;
|
||||
if (val1 > val2) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function scoretimeComparison(sub1, sub2) {
|
||||
if (!sub2) return true;
|
||||
return sub1['score'] > sub2['score'] || (sub1['score'] === sub2['score'] && isFaster(sub1['time'], sub2['time']));
|
||||
}
|
||||
|
||||
function highlightFirstSolve() {
|
||||
// bucket to store submissions by problems
|
||||
let bestSubmissions = {};
|
||||
|
||||
// get information
|
||||
$('td a').each(function() {
|
||||
let td = $(this)[0]
|
||||
let link = td['attributes']['href']['value']
|
||||
if (link.includes('submissions')) {
|
||||
let scoreAndTime = (td.innerText.split('\n'))
|
||||
let linkElements = link.split('/')
|
||||
|
||||
// get information
|
||||
let problem = linkElements[linkElements.length - 2];
|
||||
let score = parseFloat(scoreAndTime[0]);
|
||||
let time = scoreAndTime[1];
|
||||
|
||||
if (time) {
|
||||
let curSubmission = {
|
||||
'td': $(this).parent(),
|
||||
'score': score,
|
||||
'time': time
|
||||
}
|
||||
|
||||
// update best submissions
|
||||
let curBest = bestSubmissions[problem]
|
||||
|
||||
if (scoretimeComparison(curSubmission, curBest) && score) {
|
||||
bestSubmissions[problem] = curSubmission;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
for (let problem in bestSubmissions) {
|
||||
bestSubmissions[problem]['td'].addClass('first-solve')
|
||||
}
|
||||
}
|
||||
|
||||
function renew_filter() {
|
||||
var checkboxes = [
|
||||
'#show-organizations-checkbox',
|
||||
'#show-fullnames-checkbox',
|
||||
'#show-total-score-checkbox',
|
||||
];
|
||||
|
||||
var checkboxes2 = [
|
||||
'#show-friends-checkbox',
|
||||
'#show-virtual-checkbox'
|
||||
]
|
||||
|
||||
for (var i of checkboxes) {
|
||||
var $box = $(i);
|
||||
if ($box.is(':checked')) {
|
||||
$box.prop('checked', false);
|
||||
$box.click();
|
||||
$box.prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
var to_update = false;
|
||||
for (var i of checkboxes2) {
|
||||
var $box = $(i);
|
||||
if ($box.is(':checked')) {
|
||||
to_update = true;
|
||||
}
|
||||
}
|
||||
if (to_update) {
|
||||
update_ranking();
|
||||
}
|
||||
}
|
||||
|
||||
function get_initial_rank() {
|
||||
var ranks = $('.rank-td').map(function() {return this.innerHTML}).get();
|
||||
var usernames = $('.user-name .rating a').map(function() {return this.text}).get();
|
||||
window.user_rank = new Map();
|
||||
for (var i = 0; i < ranks.length; i++) {
|
||||
window.user_rank[usernames[i]] = ranks[i];
|
||||
}
|
||||
}
|
||||
|
||||
function add_initial_friend_rank() {
|
||||
var usernames = $('.user-name .rating a').map(function() {return this.text}).get();
|
||||
|
||||
var is_virtual = [];
|
||||
$('.user-name').each(function() {
|
||||
if($(this).children('sup').length) {
|
||||
is_virtual.push(1);
|
||||
}
|
||||
else is_virtual.push(0);
|
||||
});
|
||||
|
||||
$('.rank-td').each(function(i) {
|
||||
if (!is_virtual[i]) this.innerHTML += ' (' + window.user_rank[usernames[i]] + ')';
|
||||
});
|
||||
}
|
||||
|
||||
function update_ranking() {
|
||||
var friend = $('#show-friends-checkbox').is(':checked');
|
||||
var virtual = $('#show-virtual-checkbox').is(':checked');
|
||||
$('#loading-gif').show();
|
||||
$.get({
|
||||
url: `{{url('contest_ranking_ajax', contest.key)}}?friend=${friend}&virtual=${virtual}`,
|
||||
success: function(HTML) {
|
||||
$('#users-table').html(HTML);
|
||||
highlightFirstSolve();
|
||||
$('#loading-gif').hide();
|
||||
if (!virtual && !friend) {
|
||||
get_initial_rank();
|
||||
}
|
||||
if (friend) {
|
||||
add_initial_friend_rank();
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
console.log('Fail to update ranking');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$('.leaving-forever').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to leave?') }}\n' +
|
||||
|
@ -9,5 +147,68 @@
|
|||
return confirm('{{ _('Are you sure you want to join?') }}\n' +
|
||||
'{{ _('Joining a contest starts your timer, after which it becomes unstoppable.') }}');
|
||||
});
|
||||
|
||||
var url = '{{ url('contest_participation', contest.key, '__username__') }}';
|
||||
var placeholder = $('#search-contest').replaceWith($('<select>').attr({
|
||||
id: 'search-contest'
|
||||
})).attr('placeholder');
|
||||
|
||||
$('#search-contest').select2({
|
||||
placeholder: placeholder,
|
||||
ajax: {
|
||||
url: '{{ url('contest_user_search_select2_ajax', contest.key) }}'
|
||||
},
|
||||
minimumInputLength: 1,
|
||||
escapeMarkup: function (markup) {
|
||||
return markup;
|
||||
},
|
||||
templateResult: function (data, container) {
|
||||
return ('<img class="user-search-image" src="' + data.gravatar_url + '" width="24" height="24">' +
|
||||
'<span class="' + data.display_rank + ' user-search-name">' + data.text + '</span>');
|
||||
}
|
||||
}).on('change', function () {
|
||||
window.location.href = url.replace('__username__', $(this).val());
|
||||
});
|
||||
|
||||
$('#show-organizations-checkbox').click(function () {
|
||||
$('.organization-column').toggle();
|
||||
});
|
||||
$('#show-fullnames-checkbox').click(function () {
|
||||
$('.fullname-column').toggle();
|
||||
});
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
$('#show-friends-checkbox').click(function() {
|
||||
update_ranking();
|
||||
})
|
||||
{% endif %}
|
||||
$('#show-virtual-checkbox').click(function() {
|
||||
update_ranking();
|
||||
})
|
||||
$('#show-total-score-checkbox').click(function() {
|
||||
$('.problem-score-col').toggle();
|
||||
})
|
||||
|
||||
highlightFirstSolve();
|
||||
renew_filter();
|
||||
get_initial_rank();
|
||||
|
||||
{% if participation_tab %}
|
||||
$('#show-virtual-checkbox').hide();
|
||||
$('#show-virtual-label').hide();
|
||||
{% else %}
|
||||
{% if request.in_contest %}
|
||||
setInterval(update_ranking, 60 * 1000);
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
// $(".problem-score-a").on('click', function(e) {
|
||||
// var href = $(this).attr('href');
|
||||
// if (href !== '#') return;
|
||||
// e.preventDefault();
|
||||
|
||||
|
||||
// })
|
||||
});
|
||||
|
||||
</script>
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
{% block before_point_head %}
|
||||
{% for problem in problems %}
|
||||
<th class="points header problem-score-col"><a href="{{ url('contest_ranked_submissions', contest.key, problem.problem.code) }}">
|
||||
<th class="points header problem-score-col" title="{{ problem.problem.name }}"><a href="{{ url('problem_detail', problem.problem.code) }}">
|
||||
{{- contest.get_label_for_problem(loop.index0) }}
|
||||
<div class="point-denominator">{{ problem.points }}</div>
|
||||
</a></th>
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block users_media %}
|
||||
<link href="//cdn.jsdelivr.net/npm/featherlight@1.7.14/release/featherlight.min.css" type="text/css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
#content-left {
|
||||
overflow-x: auto;
|
||||
|
@ -130,6 +132,12 @@
|
|||
color: gray !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.featherlight-content {
|
||||
border-radius: 10px;
|
||||
height: 80%;
|
||||
width: 60%;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if has_rating %}
|
||||
|
@ -227,215 +235,6 @@
|
|||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<script type="text/javascript" src="{{ static('event.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function isFaster(time1, time2) {
|
||||
let arr1 = time1.split(':');
|
||||
let arr2 = time2.split(':');
|
||||
|
||||
for (let i in arr1) {
|
||||
let val1 = parseInt(arr1[i]);
|
||||
let val2 = parseInt(arr2[i]);
|
||||
if (val1 < val2) return true;
|
||||
if (val1 > val2) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function scoretimeComparison(sub1, sub2) {
|
||||
if (!sub2) return true;
|
||||
return sub1['score'] > sub2['score'] || (sub1['score'] === sub2['score'] && isFaster(sub1['time'], sub2['time']));
|
||||
}
|
||||
|
||||
function highlightFirstSolve() {
|
||||
// bucket to store submissions by problems
|
||||
let bestSubmissions = {};
|
||||
|
||||
// get information
|
||||
$('td a').each(function() {
|
||||
let td = $(this)[0]
|
||||
let link = td['attributes']['href']['value']
|
||||
if (link.includes('submissions')) {
|
||||
let scoreAndTime = (td.innerText.split('\n'))
|
||||
let linkElements = link.split('/')
|
||||
|
||||
// get information
|
||||
let problem = linkElements[linkElements.length - 2];
|
||||
let score = parseFloat(scoreAndTime[0]);
|
||||
let time = scoreAndTime[1];
|
||||
|
||||
if (time) {
|
||||
let curSubmission = {
|
||||
'td': $(this).parent(),
|
||||
'score': score,
|
||||
'time': time
|
||||
}
|
||||
|
||||
// update best submissions
|
||||
let curBest = bestSubmissions[problem]
|
||||
|
||||
if (scoretimeComparison(curSubmission, curBest) && score) {
|
||||
bestSubmissions[problem] = curSubmission;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
for (let problem in bestSubmissions) {
|
||||
bestSubmissions[problem]['td'].addClass('first-solve')
|
||||
}
|
||||
}
|
||||
|
||||
function renew_filter() {
|
||||
var checkboxes = [
|
||||
'#show-organizations-checkbox',
|
||||
'#show-fullnames-checkbox',
|
||||
'#show-total-score-checkbox',
|
||||
];
|
||||
|
||||
var checkboxes2 = [
|
||||
'#show-friends-checkbox',
|
||||
'#show-virtual-checkbox'
|
||||
]
|
||||
|
||||
for (var i of checkboxes) {
|
||||
var $box = $(i);
|
||||
if ($box.is(':checked')) {
|
||||
$box.prop('checked', false);
|
||||
$box.click();
|
||||
$box.prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
var to_update = false;
|
||||
for (var i of checkboxes2) {
|
||||
var $box = $(i);
|
||||
if ($box.is(':checked')) {
|
||||
to_update = true;
|
||||
}
|
||||
}
|
||||
if (to_update) {
|
||||
update_ranking();
|
||||
}
|
||||
}
|
||||
|
||||
function get_initial_rank() {
|
||||
var ranks = $('.rank-td').map(function() {return this.innerHTML}).get();
|
||||
var usernames = $('.user-name .rating a').map(function() {return this.text}).get();
|
||||
window.user_rank = new Map();
|
||||
for (var i = 0; i < ranks.length; i++) {
|
||||
window.user_rank[usernames[i]] = ranks[i];
|
||||
}
|
||||
}
|
||||
|
||||
function add_initial_friend_rank() {
|
||||
var usernames = $('.user-name .rating a').map(function() {return this.text}).get();
|
||||
|
||||
var is_virtual = [];
|
||||
$('.user-name').each(function() {
|
||||
if($(this).children('sup').length) {
|
||||
is_virtual.push(1);
|
||||
}
|
||||
else is_virtual.push(0);
|
||||
});
|
||||
|
||||
$('.rank-td').each(function(i) {
|
||||
if (!is_virtual[i]) this.innerHTML += ' (' + window.user_rank[usernames[i]] + ')';
|
||||
});
|
||||
}
|
||||
|
||||
function update_ranking() {
|
||||
var friend = $('#show-friends-checkbox').is(':checked');
|
||||
var virtual = $('#show-virtual-checkbox').is(':checked');
|
||||
$('#loading-gif').show();
|
||||
$.get({
|
||||
url: `/contest/{{contest.key}}/ranking/ajax?friend=${friend}&virtual=${virtual}`,
|
||||
success: function(HTML) {
|
||||
$('#users-table').html(HTML);
|
||||
highlightFirstSolve();
|
||||
$('#loading-gif').hide();
|
||||
if (!virtual && !friend) {
|
||||
get_initial_rank();
|
||||
}
|
||||
if (friend) {
|
||||
add_initial_friend_rank();
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
console.log('Fail to update ranking');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// window.load_dynamic_update = function (last_msg) {
|
||||
// return new EventReceiver(
|
||||
// "{{ EVENT_DAEMON_LOCATION }}", "{{ EVENT_DAEMON_POLL_LOCATION }}",
|
||||
// ['contest_{{contest.id}}'], last_msg, function (message) {
|
||||
// switch (message.type) {
|
||||
// case 'update':
|
||||
// update_ranking();
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
$(function () {
|
||||
var url = '{{ url('contest_participation', contest.key, '__username__') }}';
|
||||
var placeholder = $('#search-contest').replaceWith($('<select>').attr({
|
||||
id: 'search-contest'
|
||||
})).attr('placeholder');
|
||||
|
||||
$('#search-contest').select2({
|
||||
placeholder: placeholder,
|
||||
ajax: {
|
||||
url: '{{ url('contest_user_search_select2_ajax', contest.key) }}'
|
||||
},
|
||||
minimumInputLength: 1,
|
||||
escapeMarkup: function (markup) {
|
||||
return markup;
|
||||
},
|
||||
templateResult: function (data, container) {
|
||||
return ('<img class="user-search-image" src="' + data.gravatar_url + '" width="24" height="24">' +
|
||||
'<span class="' + data.display_rank + ' user-search-name">' + data.text + '</span>');
|
||||
}
|
||||
}).on('change', function () {
|
||||
window.location.href = url.replace('__username__', $(this).val());
|
||||
});
|
||||
|
||||
$('#show-organizations-checkbox').click(function () {
|
||||
$('.organization-column').toggle();
|
||||
});
|
||||
$('#show-fullnames-checkbox').click(function () {
|
||||
$('.fullname-column').toggle();
|
||||
});
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
$('#show-friends-checkbox').click(function() {
|
||||
update_ranking();
|
||||
})
|
||||
{% endif %}
|
||||
$('#show-virtual-checkbox').click(function() {
|
||||
update_ranking();
|
||||
})
|
||||
$('#show-total-score-checkbox').click(function() {
|
||||
$('.problem-score-col').toggle();
|
||||
})
|
||||
|
||||
highlightFirstSolve();
|
||||
renew_filter();
|
||||
get_initial_rank();
|
||||
{% if participation_tab %}
|
||||
$('#show-virtual-checkbox').hide();
|
||||
$('#show-virtual-label').hide();
|
||||
{% else %}
|
||||
{% if request.in_contest %}
|
||||
setInterval(update_ranking, 60 * 1000);
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
});
|
||||
</script>
|
||||
{% include "contest/media-js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -346,9 +346,6 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block description %}
|
||||
{% if can_vote and not vote %}
|
||||
{% include 'problem/voting-form.html' %}
|
||||
{% endif %}
|
||||
{% if contest_problem and contest_problem.contest.use_clarifications and has_clarifications %}
|
||||
<div id="clarification_header_container">
|
||||
<i class="fa fa-question-circle"></i>
|
||||
|
@ -374,9 +371,6 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block post_description_end %}
|
||||
{% if can_vote or request.user.is_superuser %}
|
||||
{% include 'problem/voting-controls.html' %}
|
||||
{% endif %}
|
||||
{% if request.user.is_authenticated and not request.profile.mute %}
|
||||
<a href="{{ url('new_problem_ticket', problem.code) }}" class="clarify">
|
||||
<i class="fa fa-flag" style="margin-right:0.5em"></i>
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<style>
|
||||
.featherlight .featherlight-inner{
|
||||
display: block !important;
|
||||
}
|
||||
.featherlight-content {
|
||||
overflow: inherit !important;
|
||||
}
|
||||
|
||||
.stars-container {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.stars-container .star {
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
color: orange;
|
||||
cursor: pointer;
|
||||
font-size: 30px;
|
||||
}
|
||||
.stars-container .star:before {
|
||||
content:"\f006";
|
||||
}
|
||||
.filled-star:before, .star:hover:before {
|
||||
content:"\f005" !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if can_vote or request.user.is_superuser %}
|
||||
<div class="vote-form" id="id_vote_form_box" style="display: none">
|
||||
{% include 'problem/voting-form.html' %}
|
||||
</div>
|
||||
<span>
|
||||
{% if can_vote %}
|
||||
<a href="#" class="form-button" id="id_vote_button" data-featherlight="#id_vote_form_box"></a>
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser %}
|
||||
- {% include 'problem/voting-stats.html' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<script src="{{ static('libs/featherlight/featherlight.min.js') }}" type="text/javascript"></script>
|
||||
<script>
|
||||
let voted_points = null;
|
||||
{% if vote is not none %}
|
||||
let has_voted = true;
|
||||
voted_points = {{ vote.points }};
|
||||
{% else %}
|
||||
let has_voted = false;
|
||||
{% endif %}
|
||||
|
||||
function voteUpdate(){
|
||||
$('#id_has_voted_prompt').show();
|
||||
$('#id_current_vote_value').prop('innerText', voted_points);
|
||||
$('#id_has_not_voted_prompt').hide();
|
||||
$('#id_vote_button').prop('innerText', `{{ _('Edit difficulty') }} (` + voted_points + ')');
|
||||
$('#id_points_error_box').hide();
|
||||
has_voted = true;
|
||||
}
|
||||
|
||||
function deleteVoteUpdate(){
|
||||
$('#id_has_voted_prompt').hide();
|
||||
$('#id_has_not_voted_prompt').show();
|
||||
$('#id_vote_button').prop('innerText', `{{ _('Vote difficulty') }}`);
|
||||
$('#id_points_error_box').hide();
|
||||
has_voted = false;
|
||||
}
|
||||
|
||||
if (has_voted) voteUpdate();
|
||||
else deleteVoteUpdate();
|
||||
|
||||
$('#id_vote_form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
|
@ -1,93 +0,0 @@
|
|||
<style>
|
||||
.vote_form {
|
||||
border: 3px solid blue;
|
||||
border-radius: 5px;
|
||||
width: fit-content;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0.2em 1em;
|
||||
background: #ebf0ff;
|
||||
color: #2e69ff;
|
||||
}
|
||||
</style>
|
||||
<form id="id_vote_form" class="vote_form">
|
||||
{% csrf_token %}
|
||||
<input id="id_points" class="vote-form-value" type="hidden" step="1"
|
||||
min="{{ min_possible_vote }}" max="{{ max_possible_vote }}" name="points">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-top: 1em; padding-right: 3em">
|
||||
<span><b>{{_('How difficult is this problem?')}}</b></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="vote-rating">
|
||||
<span class="stars-container">
|
||||
{% for i in range(1, (max_possible_vote - min_possible_vote) // 100 + 2) %}
|
||||
<span id="star{{i}}" star-score="{{i * 100}}" class="fa star"></span>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span>{{_('This helps us improve the site')}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span style="padding-left: 0.2em"><b>{{min_possible_vote}}</b></span>
|
||||
<span style="float: right; padding-right: 0.2em"><b>{{max_possible_vote}}</b></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="id_points_error_box">
|
||||
<span id="id_points_error" class="voting-form-error"></span>
|
||||
</div>
|
||||
<div>
|
||||
<input type="hidden" id="id_vote_form_submit_button">
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
$('.star').hover(
|
||||
(e) => $(e.target).prevAll().addClass('filled-star'),
|
||||
(e) => $(e.target).prevAll().removeClass('filled-star')
|
||||
);
|
||||
$('.star').on('click', function() {
|
||||
$("#id_points").val($(this).attr('star-score'));
|
||||
$('#id_vote_form_submit_button').click();
|
||||
$('#id_vote_form').fadeOut(500);
|
||||
})
|
||||
$('#id_vote_form_submit_button').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: '{{ url('vote', object.code) }}',
|
||||
type: 'POST',
|
||||
data: $('#id_vote_form').serialize(),
|
||||
success: function (data) {
|
||||
{% if request.user.is_superuser %}
|
||||
updateUserVote(voted_points, data.points);
|
||||
{% endif %}
|
||||
voted_points = data.points;
|
||||
voteUpdate();
|
||||
// Forms are auto disabled to prevent resubmission, but we need to allow resubmission here.
|
||||
$('#id_vote_form_submit_button').removeAttr('disabled');
|
||||
var current = $.featherlight.current();
|
||||
if (current) current.close();
|
||||
},
|
||||
error: function (data) {
|
||||
let errors = data.responseJSON;
|
||||
if(errors === undefined) {
|
||||
alert('Unable to delete vote: ' + data.responsetext);
|
||||
}
|
||||
|
||||
if('points' in errors){
|
||||
$('#id_points_error_box').show();
|
||||
$('#id_points_error').prop('textContent', errors.points[0]);
|
||||
} else {
|
||||
$('#id_points_error_box').hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -1,146 +0,0 @@
|
|||
<style>
|
||||
.vote-stats-background {
|
||||
background-color: rgb(255,255,255);
|
||||
padding: 20px;
|
||||
border-radius: 25px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
border: 1px;
|
||||
border: solid;
|
||||
border: #000000;
|
||||
}
|
||||
|
||||
.vote-stats-value {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.vote-stats-info {
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
<a href="#" class="form-button" id="id_vote_stats_button">{{ _('Statistics') }}</a>
|
||||
<div class="vote-stats-background" id="id_vote_stats" style="display: none;">
|
||||
<script src={{ static('libs/chart.js/Chart.js') }}></script>
|
||||
<span class="vote-stats-info">{{ _('Voting Statistics') }}</span>
|
||||
<br/>
|
||||
<canvas class="canvas" id="id_vote_chart"></canvas>
|
||||
<span id="id_no_votes_error" class="vote-stats-info no_votes_error">{{ _('No Votes Available!') }}</span>
|
||||
<br/>
|
||||
<div id="id_has_votes_footer" class="has_votes_footer">
|
||||
<span class="vote-stats-info">{{ _('Median:') }}</span>
|
||||
<span class="vote-stats-value median_vote" id="id_median_vote"></span>
|
||||
<span class="vote-stats-info">{{ _('Mean:') }}</span>
|
||||
<span class="vote-stats-value mean_vote" id="id_mean_vote"></span>
|
||||
<span class="vote-stats-info">{{ _('Total:') }}</span>
|
||||
<span class="vote-stats-value total_vote" id="id_num_of_votes"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let voteChart = null;
|
||||
let allVotes = {{ all_votes }};
|
||||
|
||||
function reload_vote_graph() {
|
||||
if (voteChart !== null) voteChart.destroy();
|
||||
|
||||
if (allVotes.length === 0) {
|
||||
$('.canvas').hide();
|
||||
$('.has_votes_footer').hide();
|
||||
$('.no_votes_error').show();
|
||||
} else {
|
||||
$('.canvas').show();
|
||||
$('.has_votes_footer').show();
|
||||
$('.no_votes_error').hide();
|
||||
|
||||
allVotes.sort(function(a, b){return a - b});
|
||||
// Give the graph some padding on both sides.
|
||||
let min_points = {{ min_possible_vote }};
|
||||
let max_points = {{ max_possible_vote }};
|
||||
|
||||
let xlabels = [];
|
||||
let voteFreq = [];
|
||||
for (let i = min_points; i <= max_points; i += 100) {
|
||||
xlabels.push(i);
|
||||
voteFreq.push(0);
|
||||
}
|
||||
|
||||
let max_number_of_votes = 0;
|
||||
let total_votes = 0;
|
||||
let mean = 0;
|
||||
|
||||
for (let i = 0; i < allVotes.length; i++) {
|
||||
// Assume the allVotes is valid.
|
||||
voteFreq[(allVotes[i] - min_points) / 100]++;
|
||||
max_number_of_votes = Math.max(max_number_of_votes, voteFreq[(allVotes[i] - min_points) / 100]);
|
||||
mean += allVotes[i];
|
||||
total_votes++;
|
||||
}
|
||||
mean = mean / total_votes;
|
||||
let half = Math.floor(total_votes / 2);
|
||||
let median = allVotes[half];
|
||||
if (total_votes % 2 === 0) {
|
||||
median = (median + allVotes[half - 1]) / 2;
|
||||
}
|
||||
|
||||
$('.mean_vote').prop('innerText', mean.toFixed(2));
|
||||
$('.median_vote').prop('innerText', median.toFixed(2));
|
||||
$('.total_vote').prop('innerText', total_votes);
|
||||
|
||||
const voteData = {
|
||||
labels: xlabels,
|
||||
datasets: [{
|
||||
data: voteFreq,
|
||||
backgroundColor: 'pink',
|
||||
}]
|
||||
};
|
||||
|
||||
const voteDataConfig = {
|
||||
type: 'bar',
|
||||
data: voteData,
|
||||
options: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
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($('.featherlight-inner .canvas'), voteDataConfig);
|
||||
}
|
||||
}
|
||||
|
||||
function updateUserVote(prev_voted_points, voted_points) {
|
||||
let index = allVotes.indexOf(prev_voted_points);
|
||||
if (index > -1) {
|
||||
allVotes.splice(index, 1);
|
||||
}
|
||||
allVotes.push(voted_points);
|
||||
}
|
||||
|
||||
function deleteUserVote(prev_voted_points) {
|
||||
allVotes.splice(allVotes.indexOf(prev_voted_points), 1);
|
||||
}
|
||||
$(function() {
|
||||
$('#id_vote_stats_button').featherlight('#id_vote_stats', {
|
||||
afterOpen: reload_vote_graph,
|
||||
})
|
||||
});
|
||||
</script>
|
30
templates/submission/user-ajax.html
Normal file
30
templates/submission/user-ajax.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<h4>
|
||||
{{_('Contest submissions of')}} {{link_user(profile)}} <a href="{{url('contest_user_submissions', contest.key, profile.user.username, problem.code)}}">#</a>
|
||||
</h4>
|
||||
<hr>
|
||||
<table class="lightbox-submissions"><tbody>
|
||||
{% for submission in submissions %}
|
||||
<tr>
|
||||
{% set can_view = submission_layout(submission, profile_id, request.user, editable_problem_ids, completed_problem_ids) %}
|
||||
<td>
|
||||
{% if submission.contest_time %}
|
||||
{{submission.contest_time}}
|
||||
{% else %}
|
||||
{% trans time=submission.date|date(_("N j, Y, g:i a")) %}
|
||||
{{ time }}
|
||||
{% endtrans %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="case-{{submission.result}}" style="margin-left: 1em">{{submission.display_point}}</td>
|
||||
<td class="case-{{submission.result}}" style="margin-left: 1em">({{submission.short_status}})</td>
|
||||
<td>
|
||||
[{{_('pretests') if submission.contest.is_pretest else _('main tests')}}]
|
||||
</td>
|
||||
{% if can_view %}
|
||||
<td>
|
||||
→ <a href="{{url('submission_status', submission.id)}}">{{submission.id}}</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody></table>
|
Loading…
Add table
Add a link
Reference in a new issue