diff --git a/dmoj/urls.py b/dmoj/urls.py
index fedaace..b18457c 100644
--- a/dmoj/urls.py
+++ b/dmoj/urls.py
@@ -587,6 +587,16 @@ urlpatterns = [
organization.OrganizationUsers.as_view(),
name="organization_users",
),
+ url(
+ r"^/problems$",
+ organization.OrganizationProblems.as_view(),
+ name="organization_problems",
+ ),
+ url(
+ r"^/contests$",
+ organization.OrganizationContests.as_view(),
+ name="organization_contests",
+ ),
url(
r"^/join$",
organization.JoinOrganization.as_view(),
diff --git a/judge/models/profile.py b/judge/models/profile.py
index e155a20..fe8ce17 100644
--- a/judge/models/profile.py
+++ b/judge/models/profile.py
@@ -106,6 +106,12 @@ class Organization(models.Model):
def get_users_url(self):
return reverse("organization_users", args=(self.id, self.slug))
+ def get_problems_url(self):
+ return reverse("organization_problems", args=(self.id, self.slug))
+
+ def get_contests_url(self):
+ return reverse("organization_contests", args=(self.id, self.slug))
+
class Meta:
ordering = ["name"]
permissions = (
diff --git a/judge/views/blog.py b/judge/views/blog.py
index 9ce0fdc..400ab73 100644
--- a/judge/views/blog.py
+++ b/judge/views/blog.py
@@ -69,31 +69,15 @@ class FeedView(ListView):
now = timezone.now()
- # Dashboard stuff
- # if self.request.user.is_authenticated:
- # user = self.request.profile
- # context['recently_attempted_problems'] = (Submission.objects.filter(user=user)
- # .exclude(problem__in=user_completed_ids(user))
- # .values_list('problem__code', 'problem__name', 'problem__points')
- # .annotate(points=Max('points'), latest=Max('date'))
- # .order_by('-latest')
- # [:settings.DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT])
-
visible_contests = (
Contest.get_visible_contests(self.request.user)
.filter(is_visible=True)
.order_by("start_time")
)
-
context["current_contests"] = visible_contests.filter(
start_time__lte=now, end_time__gt=now
)
context["future_contests"] = visible_contests.filter(start_time__gt=now)
-
- visible_contests = Contest.get_visible_contests(self.request.user).filter(
- is_visible=True
- )
-
context["top_rated"] = Profile.objects.filter(is_unlisted=False).order_by(
"-rating"
)[:10]
diff --git a/judge/views/contests.py b/judge/views/contests.py
index f16fdae..f179c5c 100644
--- a/judge/views/contests.py
+++ b/judge/views/contests.py
@@ -146,9 +146,15 @@ class ContestList(
self.contest_query = None
self.org_query = []
- if "orgs" in self.request.GET:
+ if "orgs" in self.request.GET and self.request.profile:
try:
self.org_query = list(map(int, request.GET.getlist("orgs")))
+ self.org_query = [
+ i
+ for i in self.org_query
+ if i
+ in self.request.profile.organizations.values_list("id", flat=True)
+ ]
except ValueError:
pass
@@ -215,7 +221,9 @@ class ContestList(
context["first_page_href"] = "."
context["contest_query"] = self.contest_query
context["org_query"] = self.org_query
- context["organizations"] = Organization.objects.all()
+ if self.request.profile:
+ context["organizations"] = self.request.profile.organizations.all()
+ context["page_type"] = "list"
context.update(self.get_sort_context())
context.update(self.get_sort_paginate_context())
return context
diff --git a/judge/views/organization.py b/judge/views/organization.py
index 2883fc0..dc96bdc 100644
--- a/judge/views/organization.py
+++ b/judge/views/organization.py
@@ -7,8 +7,14 @@ from django.core.cache.utils import make_template_fragment_key
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.db.models import Count, Q, Value, BooleanField
+from django.db.utils import ProgrammingError
from django.forms import Form, modelformset_factory
-from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect
+from django.http import (
+ Http404,
+ HttpResponsePermanentRedirect,
+ HttpResponseRedirect,
+ HttpResponseBadRequest,
+)
from django.urls import reverse
from django.utils import timezone
from django.utils.safestring import mark_safe
@@ -18,6 +24,7 @@ from django.views.generic.detail import (
SingleObjectMixin,
SingleObjectTemplateResponseMixin,
)
+from django.core.paginator import Paginator
from reversion import revisions
from judge.forms import EditOrganizationForm
@@ -37,11 +44,15 @@ from judge.utils.views import (
QueryStringSortMixin,
DiggPaginatorMixin,
)
+from judge.views.problem import ProblemList
+from judge.views.contests import ContestList
__all__ = [
"OrganizationList",
"OrganizationHome",
"OrganizationUsers",
+ "OrganizationProblems",
+ "OrganizationContests",
"OrganizationMembershipChange",
"JoinOrganization",
"LeaveOrganization",
@@ -111,6 +122,12 @@ class OrganizationDetailView(OrganizationMixin, DetailView):
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["can_edit"] = self.can_edit_organization()
+ context["is_member"] = self.is_member()
+ return context
+
class OrganizationList(TitleMixin, ListView, OrganizationBase):
model = Organization
@@ -139,19 +156,8 @@ class OrganizationList(TitleMixin, ListView, OrganizationBase):
class OrganizationHome(OrganizationDetailView):
template_name = "organization/home.html"
- def get_context_data(self, **kwargs):
- context = super(OrganizationHome, self).get_context_data(**kwargs)
- context["title"] = self.object.name
- context["can_edit"] = self.can_edit_organization()
- context["is_member"] = self.is_member()
- context["new_problems"] = Problem.objects.filter(
- is_public=True, is_organization_private=True, organizations=self.object
- ).order_by("-date", "-id")[: settings.DMOJ_BLOG_NEW_PROBLEM_COUNT]
- context["new_contests"] = Contest.objects.filter(
- is_visible=True, is_organization_private=True, organizations=self.object
- ).order_by("-end_time", "-id")[: settings.DMOJ_BLOG_NEW_CONTEST_COUNT]
-
- context["posts"] = (
+ def get_posts(self):
+ posts = (
BlogPost.objects.filter(
visible=True,
publish_on__lte=timezone.now(),
@@ -161,6 +167,21 @@ class OrganizationHome(OrganizationDetailView):
.order_by("-sticky", "-publish_on")
.prefetch_related("authors__user", "organizations")
)
+ paginator = Paginator(posts, 10)
+ page_number = self.request.GET.get("page", 1)
+ posts = paginator.get_page(page_number)
+ return posts
+
+ def get_context_data(self, **kwargs):
+ context = super(OrganizationHome, self).get_context_data(**kwargs)
+ context["title"] = self.object.name
+ context["new_problems"] = Problem.objects.filter(
+ is_public=True, is_organization_private=True, organizations=self.object
+ ).order_by("-date", "-id")[: settings.DMOJ_BLOG_NEW_PROBLEM_COUNT]
+ context["new_contests"] = Contest.objects.filter(
+ is_visible=True, is_organization_private=True, organizations=self.object
+ ).order_by("-end_time", "-id")[: settings.DMOJ_BLOG_NEW_CONTEST_COUNT]
+ context["posts"] = self.get_posts()
context["post_comment_counts"] = {
int(page[2:]): count
for page, count in Comment.objects.filter(
@@ -173,6 +194,26 @@ class OrganizationHome(OrganizationDetailView):
context["pending_count"] = OrganizationRequest.objects.filter(
state="P", organization=self.object
).count()
+
+ now = timezone.now()
+ visible_contests = (
+ Contest.get_visible_contests(self.request.user)
+ .filter(
+ is_visible=True, is_organization_private=True, organizations=self.object
+ )
+ .order_by("start_time")
+ )
+ context["current_contests"] = visible_contests.filter(
+ start_time__lte=now, end_time__gt=now
+ )
+ context["future_contests"] = visible_contests.filter(start_time__gt=now)
+ context["top_rated"] = self.object.members.filter(is_unlisted=False).order_by(
+ "-rating"
+ )[:10]
+ context["top_scorer"] = self.object.members.filter(is_unlisted=False).order_by(
+ "-performance_points"
+ )[:10]
+ context["page_type"] = "home"
return context
@@ -186,7 +227,7 @@ class OrganizationUsers(QueryStringSortMixin, OrganizationDetailView):
context = super(OrganizationUsers, self).get_context_data(**kwargs)
context["title"] = _("%s Members") % self.object.name
context["partial"] = True
- context["is_admin"] = self.can_edit_organization()
+ context["can_edit"] = self.can_edit_organization()
context["kick_url"] = reverse(
"organization_user_kick", args=[self.object.id, self.object.slug]
)
@@ -206,10 +247,76 @@ class OrganizationUsers(QueryStringSortMixin, OrganizationDetailView):
)
)
context["first_page_href"] = "."
+ context["page_type"] = "users"
context.update(self.get_sort_context())
return context
+class OrganizationExternalMixin(OrganizationBase):
+ organization_id = None
+ organization = None
+
+ def get_organization_from_url(self, request, *args, **kwargs):
+ try:
+ self.organization_id = int(kwargs["pk"])
+ self.organization = Organization.objects.get(id=self.organization_id)
+ except:
+ return HttpResponseBadRequest()
+ if self.organization.slug != kwargs["slug"]:
+ return HttpResponsePermanentRedirect(
+ request.get_full_path().replace(kwargs["slug"], self.object.slug)
+ )
+ return None
+
+ def edit_context_data(self, context):
+ context["is_member"] = self.is_member(self.organization)
+ context["can_edit"] = self.can_edit_organization(self.organization)
+ context["organization"] = self.organization
+ context["logo_override_image"] = self.organization.logo_override_image
+ context.pop("organizations")
+
+
+class OrganizationProblems(ProblemList, OrganizationExternalMixin):
+ template_name = "organization/problems.html"
+
+ def get_queryset(self):
+ self.org_query = [self.organization_id]
+ return super().get_normal_queryset()
+
+ def get(self, request, *args, **kwargs):
+ ret = super().get_organization_from_url(request, *args, **kwargs)
+ if ret:
+ return ret
+ self.setup_problem_list(request)
+ return super().get(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super(OrganizationProblems, self).get_context_data(**kwargs)
+ self.edit_context_data(context)
+ context["page_type"] = "problems"
+ return context
+
+
+class OrganizationContests(ContestList, OrganizationExternalMixin):
+ template_name = "organization/contests.html"
+
+ def get_queryset(self):
+ self.org_query = [self.organization_id]
+ return super().get_queryset()
+
+ def get(self, request, *args, **kwargs):
+ ret = super().get_organization_from_url(request, *args, **kwargs)
+ if ret:
+ return ret
+ return super().get(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super(OrganizationContests, self).get_context_data(**kwargs)
+ self.edit_context_data(context)
+ context["page_type"] = "contests"
+ return context
+
+
class OrganizationMembershipChange(
LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View
):
diff --git a/judge/views/problem.py b/judge/views/problem.py
index 1efca66..30c94b4 100644
--- a/judge/views/problem.py
+++ b/judge/views/problem.py
@@ -567,6 +567,13 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
)
]
+ def get_org_query(self, query):
+ return [
+ i
+ for i in query
+ if i in self.profile.organizations.values_list("id", flat=True)
+ ]
+
def get_normal_queryset(self):
filter = Q(is_public=True)
if self.profile is not None:
@@ -588,6 +595,7 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
).values_list("problem__id", flat=True)
)
if self.org_query:
+ self.org_query = self.get_org_query(self.org_query)
queryset = queryset.filter(
Q(organizations__in=self.org_query)
| Q(contests__contest__organizations__in=self.org_query)
@@ -650,7 +658,8 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
context["show_editorial"] = 0 if self.in_contest else int(self.show_editorial)
context["have_editorial"] = 0 if self.in_contest else int(self.have_editorial)
- context["organizations"] = Organization.objects.all()
+ if self.request.profile:
+ context["organizations"] = self.request.profile.organizations.all()
all_authors_ids = set(Problem.objects.values_list("authors", flat=True))
context["all_authors"] = Profile.objects.filter(id__in=all_authors_ids)
context["category"] = self.category
@@ -702,7 +711,7 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
("?" + self.request.GET.urlencode()) if self.request.GET else ""
)
context["first_page_href"] = (self.first_page_href or ".") + suffix
-
+ context["has_show_editorial_option"] = True
return context
def get_noui_slider_points(self):
@@ -811,6 +820,7 @@ cf_logger = logging.getLogger("judge.ml.collab_filter")
class ProblemFeed(ProblemList):
model = Problem
context_object_name = "problems"
+ template_name = "problem/feed.html"
paginate_by = 20
title = _("Problem feed")
feed_type = None
@@ -926,7 +936,8 @@ class ProblemFeed(ProblemList):
context["page_type"] = "feed"
context["title"] = self.title
context["feed_type"] = self.feed_type
-
+ context["has_show_editorial_option"] = False
+ context["has_have_editorial_option"] = False
return context
def get(self, request, *args, **kwargs):
diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po
index d2efe12..63e7ffc 100644
--- a/locale/vi/LC_MESSAGES/django.po
+++ b/locale/vi/LC_MESSAGES/django.po
@@ -4199,11 +4199,11 @@ msgstr "Quản lý"
#: templates/organization/home.html:135
msgid "Edit organization"
-msgstr "Chỉnh sửa tổ chức"
+msgstr "Chỉnh sửa"
#: templates/organization/home.html:141
msgid "View requests"
-msgstr "Xem các đơn đăng ký"
+msgstr "Đơn đăng ký"
#: templates/organization/home.html:154
msgid "Admin organization"
diff --git a/templates/blog/list.html b/templates/blog/list.html
index 98cb698..e330845 100644
--- a/templates/blog/list.html
+++ b/templates/blog/list.html
@@ -122,72 +122,7 @@
{% endif %}
- {% if current_contests %}
-
-
{{ _('Ongoing contests') }}
-
- {% for contest in current_contests %}
-
-
-
- {{ _('Ends in') }} {{ _('%(countdown)s.', countdown=contest.end_time|as_countdown) }}
-
-
- {% endfor %}
-
-
- {% endif %}
-
- {% if future_contests %}
-
-
{{ _('Upcoming contests') }}
-
- {% for contest in future_contests %}
-
-
-
- {{ _('Starting in %(countdown)s.', countdown=contest.start_time|as_countdown) }}
-
-
- {% endfor %}
-
-
- {% endif %}
-
-
{{ _('Top Rating') }}
-
-
-
- {% for user in top_rated %}
-
- {{loop.index}} |
- {{link_user(user)}} |
- {{user.rating}} |
-
- {% endfor %}
-
-
-
-
-
-
{{ _('Top Score') }}
-
-
-
- {% for user in top_scorer %}
-
- {{loop.index}} |
- {{link_user(user)}} |
- {{ user.performance_points|floatformat(0) }} |
-
- {% endfor %}
-
-
-
-
+ {% include 'contests-countdown.html' %}
+ {% include 'top-users.html' %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/contest/list.html b/templates/contest/list.html
index e0b94a7..ed8b95e 100644
--- a/templates/contest/list.html
+++ b/templates/contest/list.html
@@ -1,9 +1,9 @@
-{% extends "common-content.html" %}
+{% extends "three-column-content.html" %}
{% block meta %}
{% endblock %}
-{% block media %}
+{% block three_col_media %}
{% endblock %}
-{% block js_media %}
+{% block three_col_js %}
{% endblock %}
-{% block title_ruler %}{% endblock %}
-
-{% block title_row %}
- {% set tab = 'list' %}
- {% set title = 'Contests' %}
- {% include "contest/contest-list-tabs.html" %}
+{% block left_sidebar %}
+
{% endblock %}
{% macro contest_head(contest) %}
@@ -212,19 +219,20 @@
{% endif %}
{% endmacro %}
-{% block body %}
+{% block middle_content %}
{% if active_participations %}
diff --git a/templates/contests-countdown.html b/templates/contests-countdown.html
new file mode 100644
index 0000000..9cd2310
--- /dev/null
+++ b/templates/contests-countdown.html
@@ -0,0 +1,35 @@
+{% if current_contests %}
+
+
{{ _('Ongoing contests') }}
+
+ {% for contest in current_contests %}
+
+
+
+ {{ _('Ends in') }} {{ _('%(countdown)s.', countdown=contest.end_time|as_countdown) }}
+
+
+ {% endfor %}
+
+
+{% endif %}
+
+{% if future_contests %}
+
+
{{ _('Upcoming contests') }}
+
+ {% for contest in future_contests %}
+
+
+
+ {{ _('Starting in %(countdown)s.', countdown=contest.start_time|as_countdown) }}
+
+
+ {% endfor %}
+
+
+{% endif %}
diff --git a/templates/organization/contests.html b/templates/organization/contests.html
new file mode 100644
index 0000000..14aed86
--- /dev/null
+++ b/templates/organization/contests.html
@@ -0,0 +1,4 @@
+{% extends "contest/list.html" %}
+{% block left_sidebar %}
+ {% include "organization/org-left-sidebar.html" %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/organization/home.html b/templates/organization/home.html
index 6007abe..2be494a 100644
--- a/templates/organization/home.html
+++ b/templates/organization/home.html
@@ -1,12 +1,6 @@
-{% extends "base.html" %}
-
-{% block media %}
+{% extends "three-column-content.html" %}
+{% block three_col_media %}
{% endblock %}
-{% block js_media %}
+{% block three_col_js %}
{% endblock %}
-{% block title_row %}
+{% block left_sidebar %}
+ {% include "organization/org-left-sidebar.html" %}
+{% endblock %}
+
+{% block title_ruler %}{% endblock %}
+{% block middle_content %}
+ {% block before_posts %}{% endblock %}
{{title}}
@@ -85,81 +72,68 @@
class="unselectable button">{{ _('Request membership') }}
{% endif %}
{% endif %}
-
-{% endblock %}
-{% block title_ruler %} {% endblock %}
-{% block body %}
- {% block before_posts %}{% endblock %}
-
-
-
+ {% if is_member or can_edit %}
+ {% for post in posts %}
+ {% include "blog/content.html" %}
+ {% if posts.paginator.num_pages > 1 %}
+
{% include "list-pages.html" %}
+ {% endif %}
+ {% endfor %}
+ {% else %}
+
{{ _('About') }}
-
+
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
{{ organization.about|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
{% endcache %}
- {% if is_member or can_edit %}
-
-
{{ _('Organization news') }}
-
- {% for post in posts %}
- {% include "blog/content.html" %}
- {% else %}
-
- {{ _('There is no news at this time.') }}
-
- {% endfor %}
-
- {% endif %}
+ {% endif %}
+ {% block after_posts %}{% endblock %}
+{% endblock %}
-
- {% block after_posts %}{% endblock %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/organization/org-left-sidebar.html b/templates/organization/org-left-sidebar.html
new file mode 100644
index 0000000..7f953c3
--- /dev/null
+++ b/templates/organization/org-left-sidebar.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/templates/organization/problems.html b/templates/organization/problems.html
new file mode 100644
index 0000000..3f0ab87
--- /dev/null
+++ b/templates/organization/problems.html
@@ -0,0 +1,4 @@
+{% extends "problem/list.html" %}
+{% block left_sidebar %}
+ {% include "organization/org-left-sidebar.html" %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/organization/users.html b/templates/organization/users.html
index e960f3e..ab383a3 100644
--- a/templates/organization/users.html
+++ b/templates/organization/users.html
@@ -1,4 +1,4 @@
-{% extends "user/base-users.html" %}
+{% extends "user/base-users-three-col.html" %}
{% block users_media %}
+
+ {% if not request.in_contest_mode %}
+
+ {% endif %}
+{% endblock %}
+
+{% block three_col_js %}
+
+ {% compress js %}
+
+
+ {% endcompress %}
+ {% if request.in_contest_mode %}
+ {% compress js %}
+
+
+ {% endcompress %}
+ {% endif %}
+{% endblock %}
+
+{% block left_sidebar %}
+ {% if not request.in_contest_mode %}
+
+ {% endif %}
+{% endblock %}
+
+{% block middle_content %}
+ {% if in_contest_mode or page_type == 'list' %}
+
+
+ {% if request.in_contest_mode and request.participation.contest.use_clarifications %}
+
+ {% if can_edit_contest %}
+
+ {% endif %}
+ {% if has_clarifications %}
+
+ {{_('Clarifications')}}
+
+
+ {{_('Problem')}} |
+ {{_('Time')}} |
+ {{_('Description')}} |
+
+ {% for clarification in clarifications %}
+
+
+
+ {{ clarification.problem.name }}
+
+ |
+ {{ relative_time(clarification.date) }} |
+ {{clarification.description}} |
+
+ {% endfor %}
+
+ {% else %}
+
+ {{ _('No clarifications have been made at this time.') }}
+
+ {% endif %}
+ {% endif %}
+
+
+ {% elif page_type == 'feed' %}
+
+ {% for problem in problems %}
+ {% include "problem/feed.html" %}
+ {% endfor %}
+ {% endif %}
+ {% if page_obj.num_pages > 1 %}
+
{% include "list-pages.html" %}
+ {% endif %}
+{% endblock %}
+
+{% block right_sidebar %}
+ {% if not request.in_contest_mode %}
+
+ {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/problem/list.html b/templates/problem/list.html
index ba5ba04..fc1252a 100644
--- a/templates/problem/list.html
+++ b/templates/problem/list.html
@@ -1,450 +1,173 @@
-{% extends "three-column-content.html" %}
-{% block three_col_media %}
-
-
- {% if not request.in_contest_mode %}
-
- {% endif %}
-{% endblock %}
-
-{% block three_col_js %}
-
- {% compress js %}
-
-
- {% endcompress %}
- {% if request.in_contest_mode %}
- {% compress js %}
-
-
- {% endcompress %}
- {% endif %}
-{% endblock %}
-
+{% extends "problem/list-base.html" %}
{% block left_sidebar %}
- {% if not request.in_contest_mode %}
-
- {% endif %}
+ {% include "problem/left-sidebar.html" %}
{% endblock %}
{% block middle_content %}
- {% if in_contest_mode or page_type == 'list' %}
-
-
+ {% else %}
+
+ {{ _('No clarifications have been made at this time.') }}
+
{% endif %}
-
-
- {% elif page_type == 'feed' %}
-
- {% for problem in problems %}
- {% include "problem/feed.html" %}
- {% endfor %}
- {% endif %}
+ {% endif %}
+
{% if page_obj.num_pages > 1 %}
{% include "list-pages.html" %}
{% endif %}
-{% endblock %}
-
-{% block right_sidebar %}
- {% if not request.in_contest_mode %}
-
- {% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/problem/search-form.html b/templates/problem/search-form.html
index d762fe3..827735d 100644
--- a/templates/problem/search-form.html
+++ b/templates/problem/search-form.html
@@ -19,29 +19,32 @@
{% if show_types %} checked{% endif %}>
- {% if page_type == 'list' %}
+ {% if has_show_editorial_option %}
- {% elif page_type == 'feed' %}
+ {% endif %}
+ {% if has_have_editorial_option %}
{% endif %}
-
-
-
-
+ {% if organizations %}
+
+
+
+
+ {% endif %}
-
{% if show_types %}