Add official contest
This commit is contained in:
parent
796a670cd7
commit
10e50795d9
25 changed files with 882 additions and 362 deletions
|
@ -540,6 +540,10 @@ urlpatterns = [
|
||||||
r"^contests/summary/(?P<key>\w+)/",
|
r"^contests/summary/(?P<key>\w+)/",
|
||||||
paged_list_view(contests.ContestsSummaryView, "contests_summary"),
|
paged_list_view(contests.ContestsSummaryView, "contests_summary"),
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r"^contests/official",
|
||||||
|
paged_list_view(contests.OfficialContestList, "official_contest_list"),
|
||||||
|
),
|
||||||
url(r"^courses/", paged_list_view(course.CourseList, "course_list")),
|
url(r"^courses/", paged_list_view(course.CourseList, "course_list")),
|
||||||
url(
|
url(
|
||||||
r"^course/(?P<slug>[\w-]*)",
|
r"^course/(?P<slug>[\w-]*)",
|
||||||
|
|
|
@ -20,7 +20,12 @@ from judge.admin.problem import ProblemAdmin, ProblemPointsVoteAdmin
|
||||||
from judge.admin.profile import ProfileAdmin, UserAdmin
|
from judge.admin.profile import ProfileAdmin, UserAdmin
|
||||||
from judge.admin.runtime import JudgeAdmin, LanguageAdmin
|
from judge.admin.runtime import JudgeAdmin, LanguageAdmin
|
||||||
from judge.admin.submission import SubmissionAdmin
|
from judge.admin.submission import SubmissionAdmin
|
||||||
from judge.admin.taxon import ProblemGroupAdmin, ProblemTypeAdmin
|
from judge.admin.taxon import (
|
||||||
|
ProblemGroupAdmin,
|
||||||
|
ProblemTypeAdmin,
|
||||||
|
OfficialContestCategoryAdmin,
|
||||||
|
OfficialContestLocationAdmin,
|
||||||
|
)
|
||||||
from judge.admin.ticket import TicketAdmin
|
from judge.admin.ticket import TicketAdmin
|
||||||
from judge.admin.volunteer import VolunteerProblemVoteAdmin
|
from judge.admin.volunteer import VolunteerProblemVoteAdmin
|
||||||
from judge.admin.course import CourseAdmin
|
from judge.admin.course import CourseAdmin
|
||||||
|
@ -48,6 +53,8 @@ from judge.models import (
|
||||||
VolunteerProblemVote,
|
VolunteerProblemVote,
|
||||||
Course,
|
Course,
|
||||||
ContestsSummary,
|
ContestsSummary,
|
||||||
|
OfficialContestCategory,
|
||||||
|
OfficialContestLocation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,3 +84,5 @@ admin.site.register(Course, CourseAdmin)
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
admin.site.register(ContestsSummary, ContestsSummaryAdmin)
|
admin.site.register(ContestsSummary, ContestsSummaryAdmin)
|
||||||
|
admin.site.register(OfficialContestCategory, OfficialContestCategoryAdmin)
|
||||||
|
admin.site.register(OfficialContestLocation, OfficialContestLocationAdmin)
|
||||||
|
|
|
@ -14,7 +14,14 @@ from reversion.admin import VersionAdmin
|
||||||
from reversion_compare.admin import CompareVersionAdmin
|
from reversion_compare.admin import CompareVersionAdmin
|
||||||
|
|
||||||
from django_ace import AceWidget
|
from django_ace import AceWidget
|
||||||
from judge.models import Contest, ContestProblem, ContestSubmission, Profile, Rating
|
from judge.models import (
|
||||||
|
Contest,
|
||||||
|
ContestProblem,
|
||||||
|
ContestSubmission,
|
||||||
|
Profile,
|
||||||
|
Rating,
|
||||||
|
OfficialContest,
|
||||||
|
)
|
||||||
from judge.ratings import rate_contest
|
from judge.ratings import rate_contest
|
||||||
from judge.widgets import (
|
from judge.widgets import (
|
||||||
AdminHeavySelect2MultipleWidget,
|
AdminHeavySelect2MultipleWidget,
|
||||||
|
@ -149,6 +156,26 @@ class ContestForm(ModelForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestInlineForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
widgets = {
|
||||||
|
"category": AdminSelect2Widget,
|
||||||
|
"location": AdminSelect2Widget,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestInline(admin.StackedInline):
|
||||||
|
fields = (
|
||||||
|
"category",
|
||||||
|
"year",
|
||||||
|
"location",
|
||||||
|
)
|
||||||
|
model = OfficialContest
|
||||||
|
can_delete = True
|
||||||
|
form = OfficialContestInlineForm
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class ContestAdmin(CompareVersionAdmin):
|
class ContestAdmin(CompareVersionAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {"fields": ("key", "name", "authors", "curators", "testers")}),
|
(None, {"fields": ("key", "name", "authors", "curators", "testers")}),
|
||||||
|
@ -223,7 +250,7 @@ class ContestAdmin(CompareVersionAdmin):
|
||||||
"user_count",
|
"user_count",
|
||||||
)
|
)
|
||||||
search_fields = ("key", "name")
|
search_fields = ("key", "name")
|
||||||
inlines = [ContestProblemInline]
|
inlines = [ContestProblemInline, OfficialContestInline]
|
||||||
actions_on_top = True
|
actions_on_top = True
|
||||||
actions_on_bottom = True
|
actions_on_bottom = True
|
||||||
form = ContestForm
|
form = ContestForm
|
||||||
|
|
|
@ -56,3 +56,11 @@ class ProblemTypeAdmin(admin.ModelAdmin):
|
||||||
[o.pk for o in obj.problem_set.all()] if obj else []
|
[o.pk for o in obj.problem_set.all()] if obj else []
|
||||||
)
|
)
|
||||||
return super(ProblemTypeAdmin, self).get_form(request, obj, **kwargs)
|
return super(ProblemTypeAdmin, self).get_form(request, obj, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestCategoryAdmin(admin.ModelAdmin):
|
||||||
|
fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestLocationAdmin(admin.ModelAdmin):
|
||||||
|
fields = ("name",)
|
||||||
|
|
110
judge/migrations/0188_official_contest.py
Normal file
110
judge/migrations/0188_official_contest.py
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# Generated by Django 3.2.18 on 2024-05-30 04:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("judge", "0187_profile_info"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="OfficialContestCategory",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="official contest category",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "official contest category",
|
||||||
|
"verbose_name_plural": "official contest categories",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="OfficialContestLocation",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="official contest location",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "official contest location",
|
||||||
|
"verbose_name_plural": "official contest locations",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="OfficialContest",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("year", models.PositiveIntegerField(verbose_name="year")),
|
||||||
|
(
|
||||||
|
"category",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="judge.officialcontestcategory",
|
||||||
|
verbose_name="contest category",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"contest",
|
||||||
|
models.OneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="official",
|
||||||
|
to="judge.contest",
|
||||||
|
verbose_name="contest",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"location",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="judge.officialcontestlocation",
|
||||||
|
verbose_name="contest location",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "official contest",
|
||||||
|
"verbose_name_plural": "official contests",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -15,6 +15,9 @@ from judge.models.contest import (
|
||||||
Rating,
|
Rating,
|
||||||
ContestProblemClarification,
|
ContestProblemClarification,
|
||||||
ContestsSummary,
|
ContestsSummary,
|
||||||
|
OfficialContestCategory,
|
||||||
|
OfficialContestLocation,
|
||||||
|
OfficialContest,
|
||||||
)
|
)
|
||||||
from judge.models.interface import BlogPost, MiscConfig, NavigationBar, validate_regex
|
from judge.models.interface import BlogPost, MiscConfig, NavigationBar, validate_regex
|
||||||
from judge.models.message import PrivateMessage, PrivateMessageThread
|
from judge.models.message import PrivateMessage, PrivateMessageThread
|
||||||
|
|
|
@ -39,6 +39,9 @@ __all__ = [
|
||||||
"Rating",
|
"Rating",
|
||||||
"ContestProblemClarification",
|
"ContestProblemClarification",
|
||||||
"ContestsSummary",
|
"ContestsSummary",
|
||||||
|
"OfficialContest",
|
||||||
|
"OfficialContestCategory",
|
||||||
|
"OfficialContestLocation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -974,3 +977,53 @@ class ContestsSummary(models.Model):
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("contests_summary", args=[self.key])
|
return reverse("contests_summary", args=[self.key])
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestCategory(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=50, verbose_name=_("official contest category"), unique=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("official contest category")
|
||||||
|
verbose_name_plural = _("official contest categories")
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestLocation(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=50, verbose_name=_("official contest location"), unique=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("official contest location")
|
||||||
|
verbose_name_plural = _("official contest locations")
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContest(models.Model):
|
||||||
|
contest = models.OneToOneField(
|
||||||
|
Contest,
|
||||||
|
verbose_name=_("contest"),
|
||||||
|
related_name="official",
|
||||||
|
on_delete=CASCADE,
|
||||||
|
)
|
||||||
|
category = models.ForeignKey(
|
||||||
|
OfficialContestCategory,
|
||||||
|
verbose_name=_("contest category"),
|
||||||
|
on_delete=CASCADE,
|
||||||
|
)
|
||||||
|
year = models.PositiveIntegerField(verbose_name=_("year"))
|
||||||
|
location = models.ForeignKey(
|
||||||
|
OfficialContestLocation,
|
||||||
|
verbose_name=_("contest location"),
|
||||||
|
on_delete=CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("official contest")
|
||||||
|
verbose_name_plural = _("official contests")
|
||||||
|
|
|
@ -69,6 +69,8 @@ from judge.models import (
|
||||||
Submission,
|
Submission,
|
||||||
ContestProblemClarification,
|
ContestProblemClarification,
|
||||||
ContestsSummary,
|
ContestsSummary,
|
||||||
|
OfficialContestCategory,
|
||||||
|
OfficialContestLocation,
|
||||||
)
|
)
|
||||||
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
|
||||||
|
@ -106,6 +108,7 @@ __all__ = [
|
||||||
"base_contest_ranking_list",
|
"base_contest_ranking_list",
|
||||||
"ContestClarificationView",
|
"ContestClarificationView",
|
||||||
"update_contest_mode",
|
"update_contest_mode",
|
||||||
|
"OfficialContestList",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,8 +132,15 @@ def _find_contest(request, key):
|
||||||
|
|
||||||
|
|
||||||
class ContestListMixin(object):
|
class ContestListMixin(object):
|
||||||
|
official = False
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Contest.get_visible_contests(self.request.user)
|
q = Contest.get_visible_contests(self.request.user)
|
||||||
|
if self.official:
|
||||||
|
q = q.filter(official__isnull=False).select_related(
|
||||||
|
"official", "official__category", "official__location"
|
||||||
|
)
|
||||||
|
return q
|
||||||
|
|
||||||
|
|
||||||
class ContestList(
|
class ContestList(
|
||||||
|
@ -158,39 +168,41 @@ class ContestList(
|
||||||
return request.session.get(key, False)
|
return request.session.get(key, False)
|
||||||
return request.GET.get(key, None) == "1"
|
return request.GET.get(key, None) == "1"
|
||||||
|
|
||||||
|
def setup_contest_list(self, request):
|
||||||
|
self.contest_query = request.GET.get("contest", "")
|
||||||
|
|
||||||
|
self.hide_organization_contests = 0
|
||||||
|
if self.GET_with_session(request, "hide_organization_contests"):
|
||||||
|
self.hide_organization_contests = 1
|
||||||
|
|
||||||
|
self.org_query = []
|
||||||
|
if request.GET.get("orgs") and request.profile:
|
||||||
|
try:
|
||||||
|
self.org_query = list(map(int, request.GET.getlist("orgs")))
|
||||||
|
if not request.user.is_superuser:
|
||||||
|
self.org_query = [
|
||||||
|
i
|
||||||
|
for i in self.org_query
|
||||||
|
if i
|
||||||
|
in set(
|
||||||
|
request.profile.organizations.values_list("id", flat=True)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
default_tab = "active"
|
default_tab = "active"
|
||||||
if not self.request.user.is_authenticated:
|
if not self.request.user.is_authenticated:
|
||||||
default_tab = "current"
|
default_tab = "current"
|
||||||
self.current_tab = self.request.GET.get("tab", default_tab)
|
self.current_tab = self.request.GET.get("tab", default_tab)
|
||||||
|
|
||||||
self.contest_query = None
|
self.setup_contest_list(request)
|
||||||
self.org_query = []
|
|
||||||
self.show_orgs = 0
|
|
||||||
if self.GET_with_session(request, "show_orgs"):
|
|
||||||
self.show_orgs = 1
|
|
||||||
|
|
||||||
if self.request.GET.get("orgs") and self.request.profile:
|
|
||||||
try:
|
|
||||||
self.org_query = list(map(int, request.GET.getlist("orgs")))
|
|
||||||
if not self.request.user.is_superuser:
|
|
||||||
self.org_query = [
|
|
||||||
i
|
|
||||||
for i in self.org_query
|
|
||||||
if i
|
|
||||||
in set(
|
|
||||||
self.request.profile.organizations.values_list(
|
|
||||||
"id", flat=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return super(ContestList, self).get(request, *args, **kwargs)
|
return super(ContestList, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
to_update = ("show_orgs",)
|
to_update = ("hide_organization_contests",)
|
||||||
for key in to_update:
|
for key in to_update:
|
||||||
if key in request.GET:
|
if key in request.GET:
|
||||||
val = request.GET.get(key) == "1"
|
val = request.GET.get(key) == "1"
|
||||||
|
@ -199,6 +211,9 @@ class ContestList(
|
||||||
request.session[key] = False
|
request.session[key] = False
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
||||||
|
def extra_queryset_filters(self, queryset):
|
||||||
|
return queryset
|
||||||
|
|
||||||
def _get_queryset(self):
|
def _get_queryset(self):
|
||||||
queryset = (
|
queryset = (
|
||||||
super(ContestList, self)
|
super(ContestList, self)
|
||||||
|
@ -206,28 +221,25 @@ class ContestList(
|
||||||
.prefetch_related("tags", "organizations")
|
.prefetch_related("tags", "organizations")
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.request.GET.get("contest"):
|
if self.contest_query:
|
||||||
self.contest_query = query = " ".join(
|
substr_queryset = queryset.filter(
|
||||||
self.request.GET.getlist("contest")
|
Q(key__icontains=self.contest_query)
|
||||||
).strip()
|
| Q(name__icontains=self.contest_query)
|
||||||
if query:
|
)
|
||||||
substr_queryset = queryset.filter(
|
if settings.ENABLE_FTS:
|
||||||
Q(key__icontains=query) | Q(name__icontains=query)
|
queryset = (
|
||||||
|
queryset.search(self.contest_query).extra(order_by=["-relevance"])
|
||||||
|
| substr_queryset
|
||||||
)
|
)
|
||||||
if settings.ENABLE_FTS:
|
else:
|
||||||
queryset = (
|
queryset = substr_queryset
|
||||||
queryset.search(query).extra(order_by=["-relevance"])
|
|
||||||
| substr_queryset
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
queryset = substr_queryset
|
|
||||||
if not self.org_query and self.request.organization:
|
if not self.org_query and self.request.organization:
|
||||||
self.org_query = [self.request.organization.id]
|
self.org_query = [self.request.organization.id]
|
||||||
if self.show_orgs:
|
if self.hide_organization_contests:
|
||||||
queryset = queryset.filter(organizations=None)
|
queryset = queryset.filter(organizations=None)
|
||||||
if self.org_query:
|
if self.org_query:
|
||||||
queryset = queryset.filter(organizations__in=self.org_query)
|
queryset = queryset.filter(organizations__in=self.org_query)
|
||||||
|
queryset = self.extra_queryset_filters(queryset)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def _get_past_contests_queryset(self):
|
def _get_past_contests_queryset(self):
|
||||||
|
@ -295,10 +307,19 @@ class ContestList(
|
||||||
context["first_page_href"] = "."
|
context["first_page_href"] = "."
|
||||||
context["contest_query"] = self.contest_query
|
context["contest_query"] = self.contest_query
|
||||||
context["org_query"] = self.org_query
|
context["org_query"] = self.org_query
|
||||||
context["show_orgs"] = int(self.show_orgs)
|
context["hide_organization_contests"] = int(self.hide_organization_contests)
|
||||||
if self.request.profile:
|
if self.request.profile:
|
||||||
context["organizations"] = self.request.profile.organizations.all()
|
context["organizations"] = self.request.profile.organizations.all()
|
||||||
context["page_type"] = "list"
|
context["page_type"] = "list"
|
||||||
|
context["selected_order"] = self.request.GET.get("order")
|
||||||
|
context["all_sort_options"] = [
|
||||||
|
("start_time", _("Start time (asc.)")),
|
||||||
|
("-start_time", _("Start time (desc.)")),
|
||||||
|
("name", _("Name (asc.)")),
|
||||||
|
("-name", _("Name (desc.)")),
|
||||||
|
("user_count", _("User count (asc.)")),
|
||||||
|
("-user_count", _("User count (desc.)")),
|
||||||
|
]
|
||||||
context.update(self.get_sort_context())
|
context.update(self.get_sort_context())
|
||||||
context.update(self.get_sort_paginate_context())
|
context.update(self.get_sort_paginate_context())
|
||||||
return context
|
return context
|
||||||
|
@ -1545,3 +1566,67 @@ def recalculate_contest_summary_result(contest_summary):
|
||||||
sorted_total_points.sort(key=lambda x: x.points, reverse=True)
|
sorted_total_points.sort(key=lambda x: x.points, reverse=True)
|
||||||
total_rank = ranker(sorted_total_points)
|
total_rank = ranker(sorted_total_points)
|
||||||
return [(rank, item._asdict()) for rank, item in total_rank]
|
return [(rank, item._asdict()) for rank, item in total_rank]
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialContestList(ContestList):
|
||||||
|
official = True
|
||||||
|
template_name = "contest/official_list.html"
|
||||||
|
|
||||||
|
def setup_contest_list(self, request):
|
||||||
|
self.contest_query = request.GET.get("contest", "")
|
||||||
|
self.org_query = []
|
||||||
|
self.hide_organization_contests = False
|
||||||
|
|
||||||
|
self.selected_categories = []
|
||||||
|
self.selected_locations = []
|
||||||
|
self.year_from = None
|
||||||
|
self.year_to = None
|
||||||
|
|
||||||
|
if "category" in request.GET:
|
||||||
|
try:
|
||||||
|
self.selected_categories = list(
|
||||||
|
map(int, request.GET.getlist("category"))
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if "location" in request.GET:
|
||||||
|
try:
|
||||||
|
self.selected_locations = list(
|
||||||
|
map(int, request.GET.getlist("location"))
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if "year_from" in request.GET:
|
||||||
|
try:
|
||||||
|
self.year_from = int(request.GET.get("year_from"))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if "year_to" in request.GET:
|
||||||
|
try:
|
||||||
|
self.year_to = int(request.GET.get("year_to"))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extra_queryset_filters(self, queryset):
|
||||||
|
if self.selected_categories:
|
||||||
|
queryset = queryset.filter(official__category__in=self.selected_categories)
|
||||||
|
if self.selected_locations:
|
||||||
|
queryset = queryset.filter(official__location__in=self.selected_locations)
|
||||||
|
if self.year_from:
|
||||||
|
queryset = queryset.filter(official__year__gte=self.year_from)
|
||||||
|
if self.year_to:
|
||||||
|
queryset = queryset.filter(official__year__lte=self.year_to)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["page_type"] = "official"
|
||||||
|
context["is_official"] = True
|
||||||
|
context["categories"] = OfficialContestCategory.objects.all()
|
||||||
|
context["locations"] = OfficialContestLocation.objects.all()
|
||||||
|
context["selected_categories"] = self.selected_categories
|
||||||
|
context["selected_locations"] = self.selected_locations
|
||||||
|
context["year_from"] = self.year_from
|
||||||
|
context["year_to"] = self.year_to
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
|
@ -414,7 +414,7 @@ class OrganizationContests(
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.org_query = [self.organization_id]
|
self.org_query = [self.organization_id]
|
||||||
self.show_orgs = False
|
self.hide_organization_contests = False
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
def set_editable_contest(self, contest):
|
def set_editable_contest(self, contest):
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,6 +24,9 @@ msgstr "Giới thiệu"
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "Máy chấm"
|
msgstr "Máy chấm"
|
||||||
|
|
||||||
|
msgid "Courses"
|
||||||
|
msgstr "Khóa học"
|
||||||
|
|
||||||
msgid "Suggestions"
|
msgid "Suggestions"
|
||||||
msgstr "Đề xuất ý tưởng"
|
msgstr "Đề xuất ý tưởng"
|
||||||
|
|
||||||
|
@ -39,6 +42,9 @@ msgstr "Đăng ký tên"
|
||||||
msgid "Report"
|
msgid "Report"
|
||||||
msgstr "Báo cáo tiêu cực"
|
msgstr "Báo cáo tiêu cực"
|
||||||
|
|
||||||
|
msgid "Bug Report"
|
||||||
|
msgstr "Báo cáo lỗi"
|
||||||
|
|
||||||
msgid "2sat"
|
msgid "2sat"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -594,12 +600,6 @@ msgstr ""
|
||||||
msgid "z-function"
|
msgid "z-function"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#~ msgid "Courses"
|
|
||||||
#~ msgstr "Khóa học"
|
|
||||||
|
|
||||||
#~ msgid "Bug Report"
|
|
||||||
#~ msgstr "Báo cáo lỗi"
|
|
||||||
|
|
||||||
#~ msgid "Insert Image"
|
#~ msgid "Insert Image"
|
||||||
#~ msgstr "Chèn hình ảnh"
|
#~ msgstr "Chèn hình ảnh"
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,6 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.participate-button {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contest-title {
|
.contest-title {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<div class="left-sidebar">
|
<div class="left-sidebar">
|
||||||
{{ make_tab_item('list', 'fa fa-list', url('contest_list'), _('List')) }}
|
{{ make_tab_item('list', 'fa fa-list', url('contest_list'), _('All')) }}
|
||||||
|
{{ make_tab_item('official', 'fa fa-check-circle-o', url('official_contest_list'), _('Official')) }}
|
||||||
{{ make_tab_item('calendar', 'fa fa-calendar', url('contest_calendar', now.year, now.month), _('Calendar')) }}
|
{{ make_tab_item('calendar', 'fa fa-calendar', url('contest_calendar', now.year, now.month), _('Calendar')) }}
|
||||||
{% if perms.judge.change_contest %}
|
{% if perms.judge.change_contest %}
|
||||||
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_contest_changelist'), _('Admin')) }}
|
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_contest_changelist'), _('Admin'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
|
@ -24,7 +24,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user.is_superuser and contest_has_hidden_subtasks %}
|
{% if request.user.is_superuser and contest_has_hidden_subtasks %}
|
||||||
{{ make_tab_item('resolver', 'fa fa-check', url('resolver', contest.key), _('Resolver')) }}
|
{{ make_tab_item('resolver', 'fa fa-check', url('resolver', contest.key), _('Resolver'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if show_final_ranking %}
|
{% if show_final_ranking %}
|
||||||
{{ make_tab_item('final_ranking', 'fa fa-bar-chart', url('contest_final_ranking', contest.key), _('Final rankings')) }}
|
{{ make_tab_item('final_ranking', 'fa fa-bar-chart', url('contest_final_ranking', contest.key), _('Final rankings')) }}
|
||||||
|
@ -33,6 +33,6 @@
|
||||||
{% if perms.judge.moss_contest and has_moss_api_key %}
|
{% if perms.judge.moss_contest and has_moss_api_key %}
|
||||||
{{ make_tab_item('moss', 'fa fa-gavel', url('contest_moss', contest.key), _('MOSS')) }}
|
{{ make_tab_item('moss', 'fa fa-gavel', url('contest_moss', contest.key), _('MOSS')) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ make_tab_item('edit', 'fa fa-edit', url('admin:judge_contest_change', contest.id), _('Edit')) }}
|
{{ make_tab_item('edit', 'fa fa-edit', url('admin:judge_contest_change', contest.id), _('Edit'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,7 +21,14 @@
|
||||||
#search-org {
|
#search-org {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.contest-format-user {
|
||||||
|
flex: 0.5 !important;
|
||||||
|
}
|
||||||
|
.participate-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{% block contest_list_media %}{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block three_col_js %}
|
{% block three_col_js %}
|
||||||
|
@ -75,7 +82,7 @@
|
||||||
|
|
||||||
var $form = $('form#filter-form');
|
var $form = $('form#filter-form');
|
||||||
|
|
||||||
$('#show_orgs').click(function () {
|
$('#hide_organization_contests').click(function () {
|
||||||
submitFormWithParams($form, "POST");
|
submitFormWithParams($form, "POST");
|
||||||
});
|
});
|
||||||
$('#go').click(function() {
|
$('#go').click(function() {
|
||||||
|
@ -110,6 +117,8 @@
|
||||||
|
|
||||||
$('#search-org').select2({multiple: 1, placeholder: '{{ _('Groups') }}...'});
|
$('#search-org').select2({multiple: 1, placeholder: '{{ _('Groups') }}...'});
|
||||||
|
|
||||||
|
$('#order').select2();
|
||||||
|
|
||||||
// var tooltip_classes = 'tooltipped tooltipped-e';
|
// var tooltip_classes = 'tooltipped tooltipped-e';
|
||||||
//
|
//
|
||||||
// $('.contest-tag').each(function () {
|
// $('.contest-tag').each(function () {
|
||||||
|
@ -122,6 +131,7 @@
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{% block contest_list_js %}{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block left_sidebar %}
|
{% block left_sidebar %}
|
||||||
|
@ -134,7 +144,31 @@
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% from "contest/macros.html" import contest_head, time_left, user_count, contest_join, contest_format_user %}
|
{% from "contest/macros.html" import contest_head, time_left, user_count, contest_format_user %}
|
||||||
|
|
||||||
|
{% macro contest_join(contest, request, is_past=False) %}
|
||||||
|
{% if request.in_contest and request.participation.contest == contest %}
|
||||||
|
<button class="small" disabled>{{ _('In contest') }}</button>
|
||||||
|
{% elif is_past %}
|
||||||
|
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" class="unselectable button full small"
|
||||||
|
value="{{ _('Virtual join') }}">
|
||||||
|
</form>
|
||||||
|
{% elif request.profile.id in contest.editor_ids or request.profile.id in contest.tester_ids %}
|
||||||
|
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" class="unselectable button full small"
|
||||||
|
value="{{ _('Spectate') }}">
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" class="unselectable button full small join-warning"
|
||||||
|
value="{{ _('Join') }}">
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block middle_content %}
|
{% block middle_content %}
|
||||||
<div class="tabs tabs-no-flex" style="width: 100%;margin-left: auto;margin-right: auto;">
|
<div class="tabs tabs-no-flex" style="width: 100%;margin-left: auto;margin-right: auto;">
|
||||||
|
@ -192,11 +226,11 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-contest" style="flex: 0.5;">
|
<div class="info-contest contest-format-user">
|
||||||
{{ contest_format_user(contest, request) }}
|
{{ contest_format_user(contest, request, is_official=is_official) }}
|
||||||
</div>
|
<div class="participate-button">
|
||||||
<div class="participate-button">
|
{{ contest_join(contest, request) }}
|
||||||
{{ contest_join(contest, request) }}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
@ -230,11 +264,11 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-contest" style="flex: 0.5;">
|
<div class="info-contest contest-format-user">
|
||||||
{{ contest_format_user(contest, request) }}
|
{{ contest_format_user(contest, request, is_official=is_official) }}
|
||||||
</div>
|
<div class="participate-button">
|
||||||
<div class="participate-button">
|
{{ contest_join(contest, request) }}
|
||||||
{{ contest_join(contest, request) }}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -267,8 +301,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-contest" style="flex: 0.5;">
|
<div class="info-contest contest-format-user">
|
||||||
{{ contest_format_user(contest, request, show_user=False) }}
|
{{ contest_format_user(contest, request, show_user=False, is_official=is_official) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -296,19 +330,11 @@
|
||||||
{{ time_left(contest) }}
|
{{ time_left(contest) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-contest" style="flex: 0.5;">
|
<div class="info-contest contest-format-user">
|
||||||
{{ contest_format_user(contest, request) }}
|
{{ contest_format_user(contest, request, is_official=is_official) }}
|
||||||
</div>
|
<div class="participate-button">
|
||||||
<div class="participate-button">
|
{{ contest_join(contest, request, is_past=True) }}
|
||||||
{% if request.in_contest and request.participation.contest == contest %}
|
</div>
|
||||||
<button class="small" disabled>{{ _('In contest') }}</button>
|
|
||||||
{% else %}
|
|
||||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="submit" class="unselectable button full small"
|
|
||||||
value="{{ _('Virtual join') }}">
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -77,30 +77,24 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro contest_join(contest, request) %}
|
{% macro contest_format_user(contest, request, show_user=True, is_official=False) %}
|
||||||
{% if request.in_contest and request.participation.contest == contest %}
|
{% if is_official %}
|
||||||
<button class="small" disabled>{{ _('In contest') }}</button>
|
<div style="display: flex; flex-direction: column;">
|
||||||
{% elif request.profile.id in contest.editor_ids or request.profile.id in contest.tester_ids %}
|
<div><b>{{ _('Format') }}</b>: {{ contest.format.name }}</div>
|
||||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
<div><b>{{ _('Category') }}</b>: {{ contest.official.category.name }}</div>
|
||||||
{% csrf_token %}
|
<div><b>{{ _('Location') }}</b>: {{ contest.official.location.name }}</div>
|
||||||
<input type="submit" class="unselectable button full small"
|
<div><b>{{ _('Year') }}</b>: {{ contest.official.year }}</div>
|
||||||
value="{{ _('Spectate') }}">
|
{% if show_user %}
|
||||||
</form>
|
<div class="contest-title" style="margin-top: 0.4em">{{ user_count(contest, request.user) }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
<div style="display: flex; flex-direction: column;">
|
||||||
{% csrf_token %}
|
<div class="contest-title"> {{ _('Format') }} </div>
|
||||||
<input type="submit" class="unselectable button full small join-warning"
|
<div style="flex-grow: 1">{{ contest.format.name }}</div>
|
||||||
value="{{ _('Join') }}">
|
{% if show_user %}
|
||||||
</form>
|
<div class="contest-title" style="margin-top: 0.4em">{{ user_count(contest, request.user) }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro contest_format_user(contest, request, show_user=True) %}
|
|
||||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
|
||||||
<div class="contest-title"> {{ _('Format') }} </div>
|
|
||||||
<div style="flex-grow: 1">{{ contest.format.name }}</div>
|
|
||||||
{% if show_user %}
|
|
||||||
<div class="contest-title">{{ user_count(contest, request.user) }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
57
templates/contest/official-search-form.html
Normal file
57
templates/contest/official-search-form.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<div class="sidebox">
|
||||||
|
<h3 class="colored-text"><i class="fa fa-search"></i>{{ _('Contest search') }}</h3>
|
||||||
|
<div class="sidebox-content">
|
||||||
|
<form id="filter-form" method="GET">
|
||||||
|
<input id="search-contest" type="text" name="contest" value="{{ contest_query or '' }}"
|
||||||
|
placeholder="{{ _('Search contests...') }}">
|
||||||
|
|
||||||
|
<div class="filter-form-group">
|
||||||
|
<label class="bold-text margin-label" for="year_from"><i class="non-italics">{{ _('Year') }}</i></label>
|
||||||
|
<div class="year-range">
|
||||||
|
<input type="number" name="year_from" id="year_from" value="{{ year_from or '' }}"
|
||||||
|
placeholder="{{ _('From') }}">
|
||||||
|
<input type="number" name="year_to" id="year_to" value="{{ year_to or '' }}"
|
||||||
|
placeholder="{{ _('To') }}" style="float: right">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-form-group">
|
||||||
|
<label class="bold-text margin-label" for="category"><i class="non-italics">{{ _('Category') }}</i></label>
|
||||||
|
<select id="category" name="category" multiple>
|
||||||
|
{% for cat in categories %}
|
||||||
|
<option value="{{ cat.id }}"{% if cat.id in selected_categories %} selected{% endif %}>
|
||||||
|
{{ cat.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-form-group">
|
||||||
|
<label class="bold-text margin-label" for="location"><i class="non-italics">{{ _('Location') }}</i></label>
|
||||||
|
<select id="location" name="location" style="width: 100%" multiple>
|
||||||
|
{% for loc in locations %}
|
||||||
|
<option value="{{ loc.id }}"{% if loc.id in selected_locations %} selected{% endif %}>
|
||||||
|
{{ loc.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-form-group">
|
||||||
|
<label for="order" class="bold-text margin-label">{{ _('Order by') }}</label>
|
||||||
|
<select id="order" name="order" style="width: 100%">
|
||||||
|
<option value="">---</option>
|
||||||
|
{% for value, name in all_sort_options %}
|
||||||
|
<option value="{{value}}"{% if selected_order == value%} selected{% endif %}>
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-submit-group">
|
||||||
|
<a id="go" class="button small">{{ _('Go') }}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
42
templates/contest/official_list.html
Normal file
42
templates/contest/official_list.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{% extends "contest/list.html" %}
|
||||||
|
|
||||||
|
{% block contest_list_media %}
|
||||||
|
<style type="text/css">
|
||||||
|
.contest-format-user {
|
||||||
|
flex: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-range {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-range input {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block contest_list_js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$("#category").select2();
|
||||||
|
$("#location").select2();
|
||||||
|
$('#year_from').keypress(function (e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
$('#go').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#year_to').keypress(function (e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
$('#go').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block right_sidebar %}
|
||||||
|
<div class="right-sidebar">
|
||||||
|
{% include "contest/official-search-form.html" %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -6,8 +6,8 @@
|
||||||
placeholder="{{ _('Search contests...') }}">
|
placeholder="{{ _('Search contests...') }}">
|
||||||
{% if organizations %}
|
{% if organizations %}
|
||||||
<div style="margin-bottom: 1em;">
|
<div style="margin-bottom: 1em;">
|
||||||
<input id="show_orgs" type="checkbox" name="show_orgs" value="1" {% if show_orgs %}checked{% endif %}>
|
<input id="hide_organization_contests" type="checkbox" name="hide_organization_contests" value="1" {% if hide_organization_contests %}checked{% endif %}>
|
||||||
<label for="show_orgs">{{ _('Hide organization contests') }}</label>
|
<label for="hide_organization_contests">{{ _('Hide organization contests') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<label for="search-org" class="bold-text margin-label">{{ _('Group') }}</label>
|
<label for="search-org" class="bold-text margin-label">{{ _('Group') }}</label>
|
||||||
<select id="search-org" name="orgs" multiple>
|
<select id="search-org" name="orgs" multiple>
|
||||||
|
@ -18,6 +18,17 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="filter-form-group">
|
||||||
|
<label for="order" class="bold-text margin-label">{{ _('Order by') }}</label>
|
||||||
|
<select id="order" name="order" style="width: 100%">
|
||||||
|
<option value="">---</option>
|
||||||
|
{% for value, name in all_sort_options %}
|
||||||
|
<option value="{{value}}"{% if selected_order == value%} selected{% endif %}>
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-submit-group">
|
<div class="form-submit-group">
|
||||||
<a id="go" class="button small">{{ _('Go') }}</a>
|
<a id="go" class="button small">{{ _('Go') }}</a>
|
||||||
{% if create_url %}
|
{% if create_url %}
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
{{ make_tab_item('grades', 'fa fa-check-square-o', url('course_grades', course.slug), _('Grades')) }}
|
{{ make_tab_item('grades', 'fa fa-check-square-o', url('course_grades', course.slug), _('Grades')) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.judge.change_course %}
|
{% if perms.judge.change_course %}
|
||||||
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_course_change', course.id), _('Admin')) }}
|
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_course_change', course.id), _('Admin'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
|
@ -9,6 +9,6 @@
|
||||||
{{ make_tab_item('users', 'fa fa-user', organization.get_users_url(), _('Members')) }}
|
{{ make_tab_item('users', 'fa fa-user', organization.get_users_url(), _('Members')) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.judge.change_organization %}
|
{% if perms.judge.change_organization %}
|
||||||
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_organization_change', organization.id), _('Admin')) }}
|
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_organization_change', organization.id), _('Admin'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
|
@ -3,7 +3,7 @@
|
||||||
{{ make_tab_item('feed', 'fa fa-pagelines', url('problem_feed'), _('Feed')) }}
|
{{ make_tab_item('feed', 'fa fa-pagelines', url('problem_feed'), _('Feed')) }}
|
||||||
{{ make_tab_item('list', 'fa fa-list', url('problem_list'), _('List')) }}
|
{{ make_tab_item('list', 'fa fa-list', url('problem_list'), _('List')) }}
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_problem_changelist'), _('Admin')) }}
|
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_problem_changelist'), _('Admin'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -391,7 +391,7 @@
|
||||||
{{ make_tab_item('friend_tab', 'fa fa-users', friend_submissions_link, _('Friends')) }}
|
{{ make_tab_item('friend_tab', 'fa fa-users', friend_submissions_link, _('Friends')) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.judge.change_submission %}
|
{% if perms.judge.change_submission %}
|
||||||
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_submission_changelist'), _('Admin')) }}
|
{{ make_tab_item('admin', 'fa fa-edit', url('admin:judge_submission_changelist'), _('Admin'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -32,8 +32,15 @@
|
||||||
|
|
||||||
function navigateTo($elem, update_sidebar = false) {
|
function navigateTo($elem, update_sidebar = false) {
|
||||||
var url = $elem.attr('href');
|
var url = $elem.attr('href');
|
||||||
|
var force_new_page = $elem.data('force_new_page');
|
||||||
|
|
||||||
if (url === '#') return;
|
if (url === '#') return;
|
||||||
|
|
||||||
|
if (force_new_page) {
|
||||||
|
window.location.href = url;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (update_sidebar) {
|
if (update_sidebar) {
|
||||||
$('.left-sidebar-item').removeClass('active');
|
$('.left-sidebar-item').removeClass('active');
|
||||||
$elem.addClass('active');
|
$elem.addClass('active');
|
||||||
|
@ -99,8 +106,8 @@
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% macro make_tab_item(name, fa, url, text) %}
|
{% macro make_tab_item(name, fa, url, text, force_new_page=False) %}
|
||||||
<a class="left-sidebar-item {% if page_type == name %}active{% endif %}" href="{{ url }}" id="{{ name }}-tab">
|
<a class="left-sidebar-item {% if page_type == name %}active{% endif %}" href="{{ url }}" id="{{ name }}-tab" {% if force_new_page%}data-force_new_page="1"{% endif %}>
|
||||||
<span class="sidebar-icon"><i class="{{ fa }}"></i></span>
|
<span class="sidebar-icon"><i class="{{ fa }}"></i></span>
|
||||||
<span class="sidebar-text">{{ text }}</span>
|
<span class="sidebar-text">{{ text }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
{{ make_tab_item('friends', 'fa fa-users', url('user_list') + '?friend=true', _('Friends')) }}
|
{{ make_tab_item('friends', 'fa fa-users', url('user_list') + '?friend=true', _('Friends')) }}
|
||||||
{{ make_tab_item('organizations', 'fa fa-university', url('organization_list'), _('Groups')) }}
|
{{ make_tab_item('organizations', 'fa fa-university', url('organization_list'), _('Groups')) }}
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
{{ make_tab_item('import', 'fa fa-table', url('import_users'), _('Import')) }}
|
{{ make_tab_item('import', 'fa fa-table', url('import_users'), _('Import'), force_new_page=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
Loading…
Reference in a new issue