Change live notification to polling
This commit is contained in:
parent
2539b3e071
commit
425354a2de
7 changed files with 70 additions and 58 deletions
|
@ -1,4 +1,5 @@
|
||||||
from chat_box.views import ChatView, delete_message, post_message, chat_message_ajax, online_status_ajax
|
from chat_box.views import ChatView, delete_message, post_message, chat_message_ajax, online_status_ajax
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -226,6 +227,7 @@ urlpatterns = [
|
||||||
name='contest_participation_disqualify'),
|
name='contest_participation_disqualify'),
|
||||||
|
|
||||||
url(r'^/clarification$', contests.NewContestClarificationView.as_view(), name='new_contest_clarification'),
|
url(r'^/clarification$', contests.NewContestClarificationView.as_view(), name='new_contest_clarification'),
|
||||||
|
url(r'^/clarification/ajax$', contests.ContestClarificationAjax.as_view(), name='contest_clarification_ajax'),
|
||||||
|
|
||||||
url(r'^/$', lambda _, contest: HttpResponsePermanentRedirect(reverse('contest_view', args=[contest]))),
|
url(r'^/$', lambda _, contest: HttpResponsePermanentRedirect(reverse('contest_view', args=[contest]))),
|
||||||
])),
|
])),
|
||||||
|
|
|
@ -11,9 +11,7 @@ from django.db.models.functions import Coalesce
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from judge import event_poster as event
|
|
||||||
from judge.fulltext import SearchQuerySet
|
from judge.fulltext import SearchQuerySet
|
||||||
from judge.models.profile import Organization, Profile
|
from judge.models.profile import Organization, Profile
|
||||||
from judge.models.runtime import Language
|
from judge.models.runtime import Language
|
||||||
|
@ -409,26 +407,6 @@ class ProblemClarification(models.Model):
|
||||||
description = models.TextField(verbose_name=_('clarification body'))
|
description = models.TextField(verbose_name=_('clarification body'))
|
||||||
date = models.DateTimeField(verbose_name=_('clarification timestamp'), auto_now_add=True)
|
date = models.DateTimeField(verbose_name=_('clarification timestamp'), auto_now_add=True)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
super(ProblemClarification, self).save(*args, **kwargs)
|
|
||||||
|
|
||||||
if event.real:
|
|
||||||
from judge.models import ContestProblem
|
|
||||||
|
|
||||||
now = timezone.now()
|
|
||||||
# List all ongoing contests containing this problem
|
|
||||||
contest_problems = ContestProblem.objects.filter(
|
|
||||||
contest__start_time__lte=now,
|
|
||||||
contest__end_time__gt=now,
|
|
||||||
problem=self.problem).values_list('order', 'contest')
|
|
||||||
|
|
||||||
for order, contest_id in contest_problems.iterator():
|
|
||||||
event.post('contest_clarification_' + str(contest_id), {
|
|
||||||
'problem_label': order,
|
|
||||||
'problem_name': self.problem.name,
|
|
||||||
'problem_code': self.problem.code,
|
|
||||||
'body': self.description
|
|
||||||
})
|
|
||||||
|
|
||||||
class LanguageLimit(models.Model):
|
class LanguageLimit(models.Model):
|
||||||
problem = models.ForeignKey(Problem, verbose_name=_('problem'), related_name='language_limits', on_delete=CASCADE)
|
problem = models.ForeignKey(Problem, verbose_name=_('problem'), related_name='language_limits', on_delete=CASCADE)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.db.models import Case, Count, F, FloatField, IntegerField, Max, Min, Q, Sum, Value, When
|
from django.db.models import Case, Count, F, FloatField, IntegerField, Max, Min, Q, Sum, Value, When
|
||||||
from django.db.models.expressions import CombinedExpression
|
from django.db.models.expressions import CombinedExpression
|
||||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
|
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.template.defaultfilters import date as date_filter
|
from django.template.defaultfilters import date as date_filter
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
|
@ -915,4 +915,40 @@ class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView
|
||||||
context = super(NewContestClarificationView, self).get_context_data(**kwargs)
|
context = super(NewContestClarificationView, self).get_context_data(**kwargs)
|
||||||
context['problems'] = ContestProblem.objects.filter(contest=self.object)\
|
context['problems'] = ContestProblem.objects.filter(contest=self.object)\
|
||||||
.order_by('order')
|
.order_by('order')
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class ContestClarificationAjax(ContestMixin, DetailView):
|
||||||
|
template_name = 'contest/clarification-ajax.html'\
|
||||||
|
|
||||||
|
def is_accessible(self):
|
||||||
|
if not self.request.user.is_authenticated:
|
||||||
|
return False
|
||||||
|
if not self.request.in_contest:
|
||||||
|
return False
|
||||||
|
if not self.request.participation.contest == self.object:
|
||||||
|
return False
|
||||||
|
return self.request.user.is_superuser or \
|
||||||
|
self.request.profile in self.request.participation.contest.authors.all() or \
|
||||||
|
self.request.profile in self.request.participation.contest.curators.all()
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
if not self.is_accessible():
|
||||||
|
raise Http404()
|
||||||
|
|
||||||
|
polling_time = 1 # minute
|
||||||
|
last_one_minute = last_five_minutes = timezone.now()-timezone.timedelta(minutes=polling_time)
|
||||||
|
|
||||||
|
queryset = list(ProblemClarification.objects.filter(
|
||||||
|
problem__in=self.object.problems.all(),
|
||||||
|
date__gte=last_one_minute
|
||||||
|
).values('problem', 'problem__name', 'description'))
|
||||||
|
|
||||||
|
problems = list(ContestProblem.objects.filter(contest=self.object)\
|
||||||
|
.order_by('order').values('problem'))
|
||||||
|
problems = [i['problem'] for i in problems]
|
||||||
|
for cla in queryset:
|
||||||
|
cla['order'] = self.object.get_label_for_problem(problems.index(cla['problem']))
|
||||||
|
|
||||||
|
return JsonResponse(queryset, safe=False, json_dumps_params={'ensure_ascii': False})
|
||||||
|
|
|
@ -25,7 +25,6 @@ from django.views.generic import ListView, View
|
||||||
from django.views.generic.base import TemplateResponseMixin
|
from django.views.generic.base import TemplateResponseMixin
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from judge import event_poster as event
|
|
||||||
from judge.comments import CommentedDetailView
|
from judge.comments import CommentedDetailView
|
||||||
from judge.forms import ProblemCloneForm, ProblemSubmitForm
|
from judge.forms import ProblemCloneForm, ProblemSubmitForm
|
||||||
from judge.models import ContestProblem, ContestSubmission, Judge, Language, Problem, ProblemGroup, \
|
from judge.models import ContestProblem, ContestSubmission, Judge, Language, Problem, ProblemGroup, \
|
||||||
|
@ -174,7 +173,6 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView):
|
||||||
|
|
||||||
if contest_problem:
|
if contest_problem:
|
||||||
clarifications = self.object.clarifications
|
clarifications = self.object.clarifications
|
||||||
context['last_msg'] = event.last()
|
|
||||||
context['has_clarifications'] = clarifications.count() > 0
|
context['has_clarifications'] = clarifications.count() > 0
|
||||||
context['clarifications'] = clarifications.order_by('-date')
|
context['clarifications'] = clarifications.order_by('-date')
|
||||||
context['submission_limit'] = contest_problem.max_submissions
|
context['submission_limit'] = contest_problem.max_submissions
|
||||||
|
@ -437,7 +435,6 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
|
||||||
context['hot_problems'] = hot_problems(timedelta(days=1), 7)
|
context['hot_problems'] = hot_problems(timedelta(days=1), 7)
|
||||||
context['point_start'], context['point_end'], context['point_values'] = self.get_noui_slider_points()
|
context['point_start'], context['point_end'], context['point_values'] = self.get_noui_slider_points()
|
||||||
else:
|
else:
|
||||||
context['last_msg'] = event.last()
|
|
||||||
context['hot_problems'] = None
|
context['hot_problems'] = None
|
||||||
context['point_start'], context['point_end'], context['point_values'] = 0, 0, {}
|
context['point_start'], context['point_end'], context['point_values'] = 0, 0, {}
|
||||||
context['hide_contest_scoreboard'] = self.contest.scoreboard_visibility in \
|
context['hide_contest_scoreboard'] = self.contest.scoreboard_visibility in \
|
||||||
|
|
|
@ -324,10 +324,34 @@ window.register_notify = function (type, options) {
|
||||||
};
|
};
|
||||||
|
|
||||||
window.notify_clarification = function(msg) {
|
window.notify_clarification = function(msg) {
|
||||||
var message = `Problem ${msg.problem_label} (${msg.problem_name}):\n` + msg.body;
|
var message = `Problem ${msg.order} (${msg.problem__name}):\n` + msg.description;
|
||||||
alert(message);
|
alert(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.register_contest_notification = function(url) {
|
||||||
|
function get_clarifications() {
|
||||||
|
$.get(url)
|
||||||
|
.fail(function() {
|
||||||
|
console.log("Fail to update clarification");
|
||||||
|
})
|
||||||
|
.done(function(data) {
|
||||||
|
for (i of data) {
|
||||||
|
window.notify_clarification(i);
|
||||||
|
}
|
||||||
|
if (data.status == 403) {
|
||||||
|
console.log("Fail to retrieve data");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#chat-online-content').html(data).find('.toggle').each(function () {
|
||||||
|
register_toggle($(this));
|
||||||
|
});;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
get_clarifications();
|
||||||
|
setInterval(get_clarifications, 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
// Close dismissable boxes
|
// Close dismissable boxes
|
||||||
$("a.close").click(function () {
|
$("a.close").click(function () {
|
||||||
|
|
|
@ -132,21 +132,6 @@
|
||||||
</script>
|
</script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{% if request.in_contest %}
|
{% if request.in_contest %}
|
||||||
{% if last_msg %}
|
|
||||||
<script type="text/javascript" src="{{ static('event.js') }}"></script>
|
|
||||||
<script>
|
|
||||||
function setup_event_contest(last_msg) {
|
|
||||||
var channel = ['contest_clarification_' + {{request.participation.contest.id}}]
|
|
||||||
return new EventReceiver(
|
|
||||||
"{{ EVENT_DAEMON_LOCATION }}", "{{ EVENT_DAEMON_POLL_LOCATION }}",
|
|
||||||
channel, last_msg, function (message) {
|
|
||||||
notify_clarification(message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setup_event_contest();
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
{% compress js %}
|
{% compress js %}
|
||||||
<script src="{{ static('libs/tablesorter.js') }}" type="text/javascript"></script>
|
<script src="{{ static('libs/tablesorter.js') }}" type="text/javascript"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -174,6 +159,8 @@
|
||||||
return (node.hasClass('p') ? text.replace(/p$/, '') : text);
|
return (node.hasClass('p') ? text.replace(/p$/, '') : text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.register_contest_notification("{{url('contest_clarification_ajax', request.participation.contest.key)}}");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
|
|
|
@ -61,21 +61,9 @@
|
||||||
{% block content_js_media %}
|
{% block content_js_media %}
|
||||||
{% include "comments/media-js.html" %}
|
{% include "comments/media-js.html" %}
|
||||||
{% if request.in_contest %}
|
{% if request.in_contest %}
|
||||||
{% if last_msg %}
|
<script type="text/javascript">
|
||||||
<script type="text/javascript" src="{{ static('event.js') }}"></script>
|
window.register_contest_notification("{{url('contest_clarification_ajax', request.participation.contest.key)}}");
|
||||||
<script>
|
</script>
|
||||||
function setup_event_contest(last_msg) {
|
|
||||||
var channel = ['contest_clarification_' + {{request.participation.contest.id}}]
|
|
||||||
return new EventReceiver(
|
|
||||||
"{{ EVENT_DAEMON_LOCATION }}", "{{ EVENT_DAEMON_POLL_LOCATION }}",
|
|
||||||
channel, last_msg, function (message) {
|
|
||||||
notify_clarification(message);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setup_event_contest();
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
Loading…
Reference in a new issue