Merge pull request #11 from LQDJudge/master

Merge from master
This commit is contained in:
Phuoc Dinh Le 2021-03-24 08:14:32 -05:00 committed by GitHub
commit 948645efeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 197 additions and 46 deletions

View file

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<title>502 bad gateway - DMOJ</title> <title>502 bad gateway - LQDOJ</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css"> <style type="text/css">
hr { hr {
@ -49,14 +49,10 @@
<br> <br>
<div class="popup"> <div class="popup">
<div> <div>
<img class="logo" src="logo.png" alt="DMOJ"> <img class="logo" src="logo.png" alt="LQDOJ">
</div> </div>
<h1 style="width: 100%;">Oops, LQDOJ is down now.</h1> <h1 style="width: 100%;">Oops, LQDOJ is down now.</h1>
</div> </div>
<br>
<hr>
<br>
<h2 class="msg">But don't worry, we'll be back soon. <br> In the free time, you can read my idol's codes <a href="http://codeforces.com/profile/cuom1999" style="color:red; text-decoration:none">cuom1999</a></h2>
</div> </div>
</body> </body>
</html> </html>

View file

@ -31,7 +31,7 @@ from judge import event_poster as event
from judge.comments import CommentedDetailView from judge.comments import CommentedDetailView
from judge.forms import ContestCloneForm from judge.forms import ContestCloneForm
from judge.models import Contest, ContestMoss, ContestParticipation, ContestProblem, ContestTag, \ from judge.models import Contest, ContestMoss, ContestParticipation, ContestProblem, ContestTag, \
Problem, Profile, Submission Organization, Problem, Profile, Submission
from judge.tasks import run_moss from judge.tasks import run_moss
from judge.utils.celery import redirect_to_task_status from judge.utils.celery import redirect_to_task_status
from judge.utils.opengraph import generate_opengraph from judge.utils.opengraph import generate_opengraph
@ -81,15 +81,38 @@ class ContestList(DiggPaginatorMixin, TitleMixin, ContestListMixin, ListView):
template_name = 'contest/list.html' template_name = 'contest/list.html'
title = gettext_lazy('Contests') title = gettext_lazy('Contests')
context_object_name = 'past_contests' context_object_name = 'past_contests'
first_page_href = None
@cached_property @cached_property
def _now(self): def _now(self):
return timezone.now() return timezone.now()
def get(self, request, *args, **kwargs):
self.contest_query = None
self.org_query = []
if 'orgs' in self.request.GET:
try:
self.org_query = list(map(int, request.GET.getlist('orgs')))
except ValueError:
pass
return super(ContestList, self).get(request, *args, **kwargs)
def _get_queryset(self): def _get_queryset(self):
return super(ContestList, self).get_queryset() \ queryset = super(ContestList, self).get_queryset() \
.order_by('-start_time', 'key').prefetch_related('tags', 'organizations', 'organizers') .order_by('-start_time', 'key').prefetch_related('tags', 'organizations', 'organizers')
if 'contest' in self.request.GET:
self.contest_query = query = ' '.join(self.request.GET.getlist('contest')).strip()
if query:
queryset = queryset.filter(
Q(key__icontains=query) | Q(name__icontains=query))
if self.org_query:
queryset = queryset.filter(organizations__in=self.org_query)
return queryset
def get_queryset(self): def get_queryset(self):
return self._get_queryset().filter(end_time__lt=self._now) return self._get_queryset().filter(end_time__lt=self._now)
@ -117,6 +140,12 @@ class ContestList(DiggPaginatorMixin, TitleMixin, ContestListMixin, ListView):
context['future_contests'] = future context['future_contests'] = future
context['now'] = self._now context['now'] = self._now
context['first_page_href'] = '.' context['first_page_href'] = '.'
context['contest_query'] = self.contest_query
context['org_query'] = self.org_query
context['organizations'] = Organization.objects.all()
context['page_suffix'] = suffix = (
'?' + self.request.GET.urlencode()) if self.request.GET else ''
context['first_page_href'] = (self.first_page_href or '.') + suffix
return context return context
@ -593,7 +622,8 @@ def base_contest_ranking_list(contest, problems, queryset):
def contest_ranking_list(contest, problems): def contest_ranking_list(contest, problems):
return base_contest_ranking_list(contest, problems, contest.users.filter(virtual=0, user__is_unlisted=False) return base_contest_ranking_list(contest, problems, contest.users.filter(virtual=0, user__is_unlisted=False)
.prefetch_related('user__organizations') .prefetch_related('user__organizations')
.order_by('is_disqualified', '-score', 'cumtime')) .extra(select={'round_score': 'round(score, 6)'})
.order_by('is_disqualified', '-round_score', 'cumtime'))
def get_contest_ranking_list(request, contest, participation=None, ranking_list=contest_ranking_list, def get_contest_ranking_list(request, contest, participation=None, ranking_list=contest_ranking_list,

View file

@ -29,7 +29,7 @@ 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, \
ProblemTranslation, ProblemType, RuntimeVersion, Solution, Submission, SubmissionSource, \ ProblemTranslation, ProblemType, RuntimeVersion, Solution, Submission, SubmissionSource, \
TranslatedProblemForeignKeyQuerySet TranslatedProblemForeignKeyQuerySet, Organization
from judge.pdf_problems import DefaultPdfMaker, HAS_PDF from judge.pdf_problems import DefaultPdfMaker, HAS_PDF
from judge.utils.diggpaginator import DiggPaginator from judge.utils.diggpaginator import DiggPaginator
from judge.utils.opengraph import generate_opengraph from judge.utils.opengraph import generate_opengraph
@ -374,6 +374,10 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
if self.profile is not None and self.hide_solved: if self.profile is not None and self.hide_solved:
queryset = queryset.exclude(id__in=Submission.objects.filter(user=self.profile, points=F('problem__points')) queryset = queryset.exclude(id__in=Submission.objects.filter(user=self.profile, points=F('problem__points'))
.values_list('problem__id', flat=True)) .values_list('problem__id', flat=True))
if self.org_query:
queryset = queryset.filter(
Q(organizations__in=self.org_query) |
Q(contests__contest__organizations__in=self.org_query))
if self.show_types: if self.show_types:
queryset = queryset.prefetch_related('types') queryset = queryset.prefetch_related('types')
if self.category is not None: if self.category is not None:
@ -414,12 +418,14 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
context['full_text'] = 0 if self.in_contest else int(self.full_text) context['full_text'] = 0 if self.in_contest else int(self.full_text)
context['show_editorial'] = 0 if self.in_contest else int(self.show_editorial) context['show_editorial'] = 0 if self.in_contest else int(self.show_editorial)
context['organizations'] = Organization.objects.all()
context['category'] = self.category context['category'] = self.category
context['categories'] = ProblemGroup.objects.all() context['categories'] = ProblemGroup.objects.all()
if self.show_types: if self.show_types:
context['selected_types'] = self.selected_types context['selected_types'] = self.selected_types
context['problem_types'] = ProblemType.objects.all() context['problem_types'] = ProblemType.objects.all()
context['has_fts'] = settings.ENABLE_FTS context['has_fts'] = settings.ENABLE_FTS
context['org_query'] = self.org_query
context['search_query'] = self.search_query context['search_query'] = self.search_query
context['completed_problem_ids'] = self.get_completed_problems() context['completed_problem_ids'] = self.get_completed_problems()
context['attempted_problems'] = self.get_attempted_problems() context['attempted_problems'] = self.get_attempted_problems()
@ -467,6 +473,7 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
self.search_query = None self.search_query = None
self.category = None self.category = None
self.org_query = []
self.selected_types = [] self.selected_types = []
# This actually copies into the instance dictionary... # This actually copies into the instance dictionary...
@ -481,6 +488,12 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
except ValueError: except ValueError:
pass pass
if 'orgs' in request.GET:
try:
self.org_query = list(map(int, request.GET.getlist('orgs')))
except ValueError:
pass
self.point_start = safe_float_or_none(request.GET.get('point_start')) self.point_start = safe_float_or_none(request.GET.get('point_start'))
self.point_end = safe_float_or_none(request.GET.get('point_end')) self.point_end = safe_float_or_none(request.GET.get('point_end'))

View file

@ -14,6 +14,58 @@
.content-description ul { .content-description ul {
padding: 0 !important; padding: 0 !important;
} }
.btn-contest {
display: inline-block;
padding: 1px 6px;
}
{% if page_obj and page_obj.number > 1%}
#ongoing-table {
display: none;
}
{% endif %}
@media (max-width: 500px) {
#search-contest, #search-org, #search-btn {
width: 100%;
margin-bottom: 0.5em;
}
#search-contest {
height: 2.5em;
}
#search-btn {
margin-top: 0.5em;
}
#filter-form input {
padding-left: 8px;
}
}
@media (min-width: 500px) {
#filter-form input {
margin: 0 0.5em 0 0!important;
padding-left: 8px;
padding-top: 8px;
}
#search-contest {
width: 30%;
height: 2.3em;
margin-right: 1em;
margin-bottom: 0;
padding-top: 4px !important;
}
#search-org {
width: 40%;
}
#search-btn {
display: inline-block;
height: 2.3em;
margin-left: 0.5em;
}
}
</style> </style>
{% endblock %} {% endblock %}
@ -32,6 +84,24 @@
'{{ _('Joining a contest for the first time starts your timer, after which it becomes unstoppable.') }}'); '{{ _('Joining a contest for the first time starts your timer, after which it becomes unstoppable.') }}');
}); });
function makeToggleBtn(btn, divElement) {
btn.click(function() {
divElement.toggle(300);
if (btn.html().trim() === '{{ _('Hide')}}') {
btn.html('{{ _('Show')}}');
}
else {
btn.html('{{ _('Hide')}}');
}
});
}
makeToggleBtn($("#ongoing-btn"), $("#ongoing-table"));
$('#search-org').select2({multiple: 1, placeholder: '{{ _('Organizations...') }}'})
.css({'visibility': 'visible'});
// var tooltip_classes = 'tooltipped tooltipped-e'; // var tooltip_classes = 'tooltipped tooltipped-e';
// //
// $('.contest-tag').each(function () { // $('.contest-tag').each(function () {
@ -144,6 +214,20 @@
{% block body %} {% block body %}
<div class="content-description"> <div class="content-description">
<form id="filter-form">
<input id="search-contest" type="text" name="contest" value="{{ contest_query or '' }}"
placeholder="{{ _('Search contests...') }}">
<select id="search-org" name="orgs" multiple>
{% for org in organizations %}
<option value="{{ org.id }}"{% if org.id in org_query %} selected{% endif %}>
{{ org.name }}
</option>
{% endfor %}
</select>
<button id="search-btn"> {{ _('Search')}} </button>
</form>
{% if active_participations %} {% if active_participations %}
<h4>{{ _('Active Contests') }}</h4> <h4>{{ _('Active Contests') }}</h4>
<table class="contest-list table striped"> <table class="contest-list table striped">
@ -187,41 +271,52 @@
{% endif %} {% endif %}
{% if current_contests %} {% if current_contests %}
<h4>{{ _('Ongoing Contests') }}</h4> <h4>
<table class="contest-list table striped"> {{ _('Ongoing Contests') }}
<thead> <button class="btn-contest" id="ongoing-btn">
<tr> {% if page_obj and page_obj.number > 1%}
<th style="width:90%">{{ _('Contest') }}</th> {{_('Show')}}
<th>{{ _('Users') }}</th> {% else %}
{% if not request.in_contest %} {{_('Hide')}}
<th style="width:15%"></th>
{% endif %} {% endif %}
</tr> </button>
</thead> </h4>
<tbody> <div id="ongoing-table">
{% for contest in current_contests %} <table class="contest-list table striped">
<thead>
<tr> <tr>
<td> <th style="width:90%">{{ _('Contest') }}</th>
<div class="contest-block"> <th>{{ _('Users') }}</th>
{{ contest_head(contest) }} {% if not request.in_contest %}
{% if contest.start_time %} <th style="width:15%"></th>
<br> {% endif %}
{% if contest.time_before_end %}
<span class="time">{{ _('Ends in %(countdown)s', countdown=contest.time_before_end|as_countdown) }}</span>
{% endif %}
{{ time_left(contest) }}
{% endif %}
</div>
</td>
<td>
{{ user_count(contest, request.user) }}
</td>
{{ contest_join(contest, request) }}
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for contest in current_contests %}
<br> <tr>
<td>
<div class="contest-block">
{{ contest_head(contest) }}
{% if contest.start_time %}
<br>
{% if contest.time_before_end %}
<span class="time">{{ _('Ends in %(countdown)s', countdown=contest.time_before_end|as_countdown) }}</span>
{% endif %}
{{ time_left(contest) }}
{% endif %}
</div>
</td>
<td>
{{ user_count(contest, request.user) }}
</td>
{{ contest_join(contest, request) }}
</tr>
{% endfor %}
</tbody>
</table>
<br>
</div>
{% endif %} {% endif %}
<h4>{{ _('Upcoming Contests') }}</h4> <h4>{{ _('Upcoming Contests') }}</h4>

View file

@ -3,13 +3,17 @@
{% block media %} {% block media %}
<link rel="stylesheet" href="{{ static('libs/nouislider.min.css') }}"> <link rel="stylesheet" href="{{ static('libs/nouislider.min.css') }}">
<noscript> <noscript>
<style>#category, #types { <style>
visibility: visible; #category, #types {
} visibility: visible;
}
</style> </style>
</noscript> </noscript>
{% if not request.in_contest %} {% if not request.in_contest %}
<style> <style>
#search-org {
width: 100%;
}
#problem-table th { #problem-table th {
padding: 0; padding: 0;
} }
@ -61,6 +65,8 @@
$category.select2().css({'visibility': 'visible'}).change(clean_submit); $category.select2().css({'visibility': 'visible'}).change(clean_submit);
$('#types').select2({multiple: 1, placeholder: '{{ _('Filter by type...') }}'}) $('#types').select2({multiple: 1, placeholder: '{{ _('Filter by type...') }}'})
.css({'visibility': 'visible'}); .css({'visibility': 'visible'});
$('#search-org').select2({multiple: 1, placeholder: '{{ _('Organizations...') }}'})
.css({'visibility': 'visible'});
// This is incredibly nasty to do but it's needed because otherwise the select2 steals the focus // This is incredibly nasty to do but it's needed because otherwise the select2 steals the focus
$search.keypress(function (e) { $search.keypress(function (e) {

View file

@ -31,6 +31,16 @@
{% if show_editorial %} checked{% endif %}> {% if show_editorial %} checked{% endif %}>
<label for="show_editorial">{{ _('Show editorial') }}</label> <label for="show_editorial">{{ _('Show editorial') }}</label>
</div> </div>
<div class="filter-form-group">
<label for="type"><i>{{ _('Organization') }}</i></label>
<select id="search-org" name="orgs" multiple>
{% for org in organizations %}
<option value="{{ org.id }}"{% if org.id in org_query %} selected{% endif %}>
{{ org.name }}
</option>
{% endfor %}
</select>
</div>
<div class="filter-form-group"> <div class="filter-form-group">
<label for="category"><i>{{ _('Category') }}</i></label> <label for="category"><i>{{ _('Category') }}</i></label>
<select id="category" name="category"> <select id="category" name="category">
@ -45,6 +55,7 @@
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% if show_types %} {% if show_types %}
<div class="filter-form-group"> <div class="filter-form-group">