From e2d7421b2fd76734805ebffa208fcb33b199276b Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 16:44:29 +0200
Subject: [PATCH 01/29] Add ProblemEditorial class like ProblemStatement class.
---
oioioi/problems/models.py | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/oioioi/problems/models.py b/oioioi/problems/models.py
index 0f3687eb6..ae4c9c3a0 100644
--- a/oioioi/problems/models.py
+++ b/oioioi/problems/models.py
@@ -257,6 +257,40 @@ def __str__(self):
return '%s / %s' % (self.problem.name, self.filename)
+class ProblemEditorial(models.Model):
+ """Represents a file containing problem editoral.
+
+ Problem may have multiple editorials, for example in various languages
+ or formats.
+ """
+
+ problem = models.ForeignKey(
+ Problem, related_name='editorials', on_delete=models.CASCADE
+ )
+ language = models.CharField(
+ max_length=6, blank=True, null=True, verbose_name=_("language code")
+ )
+ content = FileField(upload_to=make_problem_filename, verbose_name=_("content"))
+
+ @property
+ def filename(self):
+ return os.path.split(self.content.name)[1]
+
+ @property
+ def download_name(self):
+ return self.problem.short_name + self.extension
+
+ @property
+ def extension(self):
+ return os.path.splitext(self.content.name)[1].lower()
+
+ class Meta(object):
+ verbose_name = _("problem editorial")
+ verbose_name_plural = _("problem editorials")
+
+ def __str__(self):
+ return '%s / %s' % (self.problem.name, self.filename)
+
class ProblemAttachment(models.Model):
"""Represents an additional file visible to the contestant, linked to
From 9266f73bb805514ce16624f69d9fd5d3a0c2a5da Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 17:16:25 +0200
Subject: [PATCH 02/29] Add initial, stupid editorial tab implementation.
---
oioioi/problems/problem_site.py | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 9e9be53e8..e33d01a4f 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -34,6 +34,7 @@
ProblemAttachment,
ProblemPackage,
ProblemStatement,
+ ProblemEditorial,
)
from oioioi.problems.problem_sources import UploadedPackageSource
from oioioi.problems.utils import (
@@ -308,6 +309,29 @@ def problem_site_add_to_contest(request, problem):
)
+def show_editorial(request, problem):
+ """Show Editorial tab only if there's ≥1 ProblemEditorial."""
+ return ProblemEditorial.objects.filter(problem=problem).exists() and not request.contest
+
+@problem_site_tab(
+ _("Editorial"),
+ key='editorial',
+ order=750,
+ condition=show_editorial,
+)
+def problem_site_editorial(request, problem):
+ editorial = ProblemEditorial.objects.filter(problem=problem).order_by('language', 'id').first()
+ return TemplateResponse(
+ request,
+ 'problems/editorial.html',
+ {
+ 'problem': problem,
+ 'editorial': editorial,
+ 'can_admin_problem': can_admin_problem(request, problem),
+ }
+ )
+
+
@problem_site_tab(
_("Replace problem statement"),
key='replace_problem_statement',
From af79e581fd203a0ac05d4ee585e2913d85bb2ee5 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 17:34:53 +0200
Subject: [PATCH 03/29] Abstract away query_statement contents into
query_document and call it in query_statement and query_editorial.
---
oioioi/problems/utils.py | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/oioioi/problems/utils.py b/oioioi/problems/utils.py
index 3f7fd17fc..088e944cb 100644
--- a/oioioi/problems/utils.py
+++ b/oioioi/problems/utils.py
@@ -20,6 +20,7 @@
from oioioi.problems.models import (
AlgorithmTagProposal,
DifficultyTagProposal,
+ ProblemEditorial,
ProblemStatement,
ProblemStatistics,
UserStatistics,
@@ -131,9 +132,8 @@ def can_add_to_problemset(request):
return request.user.has_perm('problems.problems_db_admin')
-def query_statement(problem_id):
- statements = ProblemStatement.objects.filter(problem=problem_id)
- if not statements:
+def query_document(documents):
+ if not documents:
return None
lang_prefs = (
@@ -152,7 +152,15 @@ def sort_key(statement):
ext_pref = (sys.maxsize, statement.extension)
return lang_pref, ext_pref
- return sorted(statements, key=sort_key)[0]
+ return sorted(documents, key=sort_key)[0]
+
+def query_statement(problem_id):
+ statements = ProblemStatement.objects.filter(problem=problem_id)
+ return query_document(statements)
+
+def query_editorial(problem_id):
+ editorials = ProblemEditorial.objects.filter(problem=problem_id)
+ return query_document(editorials)
def query_zip(statement, path):
From 1d737b915fd0bb82d4dd374660a0547d23e4a9da Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 17:41:45 +0200
Subject: [PATCH 04/29] Move editorial tab closer to statement.
---
oioioi/problems/problem_site.py | 43 +++++++++++++++------------------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index e33d01a4f..b9ff477c2 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -129,6 +129,26 @@ def problem_site_statement(request, problem):
return statement_html
+def show_editorial(request, problem):
+ """Show Editorial tab only if there's ≥1 ProblemEditorial."""
+ return ProblemEditorial.objects.filter(problem=problem).exists() and not request.contest
+
+@problem_site_tab(
+ _("Editorial"), key='editorial', order=750, condition=show_editorial
+)
+def problem_site_editorial(request, problem):
+ editorial = ProblemEditorial.objects.filter(problem=problem).order_by('language', 'id').first()
+ return TemplateResponse(
+ request,
+ 'problems/editorial.html',
+ {
+ 'problem': problem,
+ 'editorial': editorial,
+ 'can_admin_problem': can_admin_problem(request, problem),
+ }
+ )
+
+
def check_for_downloads(request, problem):
"""Function checking if given problem has any downloadables."""
return bool(ProblemAttachment.objects.filter(problem=problem)) or bool(
@@ -309,29 +329,6 @@ def problem_site_add_to_contest(request, problem):
)
-def show_editorial(request, problem):
- """Show Editorial tab only if there's ≥1 ProblemEditorial."""
- return ProblemEditorial.objects.filter(problem=problem).exists() and not request.contest
-
-@problem_site_tab(
- _("Editorial"),
- key='editorial',
- order=750,
- condition=show_editorial,
-)
-def problem_site_editorial(request, problem):
- editorial = ProblemEditorial.objects.filter(problem=problem).order_by('language', 'id').first()
- return TemplateResponse(
- request,
- 'problems/editorial.html',
- {
- 'problem': problem,
- 'editorial': editorial,
- 'can_admin_problem': can_admin_problem(request, problem),
- }
- )
-
-
@problem_site_tab(
_("Replace problem statement"),
key='replace_problem_statement',
From b5dd303104c690b8ceb2c12bbbb5eb23a848764c Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 18:15:03 +0200
Subject: [PATCH 05/29] Partially-correct abstraction of problem_site_statement
-> problem_site_document.
---
oioioi/problems/problem_site.py | 40 ++++++++++++++-------------------
1 file changed, 17 insertions(+), 23 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index b9ff477c2..7254fd150 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -42,6 +42,7 @@
can_admin_problem,
generate_add_to_contest_metadata,
generate_model_solutions_context,
+ query_editorial,
query_statement,
query_zip,
)
@@ -88,23 +89,14 @@ def problem_site_statement_zip_view(request, site_key, path):
return query_zip(statement, path)
-def check_for_statement(request, problem):
- """Function checking if given problem has a ProblemStatement."""
- return bool(ProblemStatement.objects.filter(problem=problem))
-
-
-@problem_site_tab(
- _("Problem statement"), key='statement', order=100, condition=check_for_statement
-)
-def problem_site_statement(request, problem):
- statement = query_statement(problem.id)
- if not statement:
+def problem_site_document(request, problem, document):
+ if not document:
statement_html = render_to_string(
'problems/no-problem-statement.html',
{'problem': problem,
'can_admin_problem': can_admin_problem(request, problem)}
)
- elif statement.extension == '.zip':
+ elif document.extension == '.zip':
response = problem_site_statement_zip_view(
request, problem.problemsite.url_key, 'index.html'
)
@@ -128,25 +120,27 @@ def problem_site_statement(request, problem):
return statement_html
+def check_for_statement(request, problem):
+ """Function checking if given problem has a ProblemStatement."""
+ return bool(ProblemStatement.objects.filter(problem=problem))
+
+@problem_site_tab(
+ _("Problem statement"), key='statement', order=100, condition=check_for_statement
+)
+def problem_site_statement(request, problem):
+ statement = query_statement(problem.id)
+ return problem_site_document(request, problem, statement)
+
def show_editorial(request, problem):
- """Show Editorial tab only if there's ≥1 ProblemEditorial."""
return ProblemEditorial.objects.filter(problem=problem).exists() and not request.contest
@problem_site_tab(
_("Editorial"), key='editorial', order=750, condition=show_editorial
)
def problem_site_editorial(request, problem):
- editorial = ProblemEditorial.objects.filter(problem=problem).order_by('language', 'id').first()
- return TemplateResponse(
- request,
- 'problems/editorial.html',
- {
- 'problem': problem,
- 'editorial': editorial,
- 'can_admin_problem': can_admin_problem(request, problem),
- }
- )
+ statement = query_editorial(problem.id)
+ return problem_site_document(request, problem, statement)
def check_for_downloads(request, problem):
From 5e143a2a76ebe1548a8a3e32beb916b3bd820b5d Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 18:21:13 +0200
Subject: [PATCH 06/29] Better check_for_statement implementation.
---
oioioi/problems/problem_site.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 7254fd150..8b4a53b88 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -121,8 +121,7 @@ def problem_site_document(request, problem, document):
return statement_html
def check_for_statement(request, problem):
- """Function checking if given problem has a ProblemStatement."""
- return bool(ProblemStatement.objects.filter(problem=problem))
+ return ProblemStatement.objects.filter(problem=problem).exists()
@problem_site_tab(
_("Problem statement"), key='statement', order=100, condition=check_for_statement
From b46875b72c8e6be6a38f01cda36a2e0fe5accc21 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 18:32:17 +0200
Subject: [PATCH 07/29] Add no-problem-editorial.html template.
---
.../templates/problems/no-problem-editorial.html | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 oioioi/problems/templates/problems/no-problem-editorial.html
diff --git a/oioioi/problems/templates/problems/no-problem-editorial.html b/oioioi/problems/templates/problems/no-problem-editorial.html
new file mode 100644
index 000000000..dca8ec537
--- /dev/null
+++ b/oioioi/problems/templates/problems/no-problem-editorial.html
@@ -0,0 +1,15 @@
+{% load i18n %}
+
+
+ {% blocktrans %}
+
This problem doesn't have a publicly available editorial.
+ {% endblocktrans %}
+
+
+{% if can_admin_problem %}
+
+ {% blocktrans %}
Problem's author:
{% endblocktrans %}
+
{{ problem.author.first_name }} {{problem.author.last_name}} ({{problem.author.username}})
+
Send e-mail to the author
+
+{% endif %}
From b43e247ca5d676808c061f4710a2e9031ca5508a Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 18:35:44 +0200
Subject: [PATCH 08/29] Load different page depending on document type.
---
oioioi/problems/problem_site.py | 31 ++++++++++++++++++++-----------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 8b4a53b88..a1cc78597 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -89,18 +89,27 @@ def problem_site_statement_zip_view(request, site_key, path):
return query_zip(statement, path)
-def problem_site_document(request, problem, document):
+def problem_site_document(request, problem, document, type):
if not document:
- statement_html = render_to_string(
- 'problems/no-problem-statement.html',
- {'problem': problem,
- 'can_admin_problem': can_admin_problem(request, problem)}
- )
+ if type == 'statement':
+ document_html = render_to_string(
+ 'problems/no-problem-statement.html',
+ {'problem': problem,
+ 'can_admin_problem': can_admin_problem(request, problem)}
+ )
+ elif type == 'editorial':
+ document_html = render_to_string(
+ 'problems/no-problem-editorial.html',
+ {'problem': problem,
+ 'can_admin_problem': can_admin_problem(request, problem)}
+ )
+ else:
+ raise Http404("Document not found")
elif document.extension == '.zip':
response = problem_site_statement_zip_view(
request, problem.problemsite.url_key, 'index.html'
)
- statement_html = render_to_string(
+ document_html = render_to_string(
'problems/from-zip-statement.html',
{'problem': problem,
'statement': mark_safe(response.content.decode(errors="replace")),
@@ -111,14 +120,14 @@ def problem_site_document(request, problem, document):
'problem_site_external_statement',
kwargs={'site_key': problem.problemsite.url_key},
)
- statement_html = render_to_string(
+ document_html = render_to_string(
'problems/external-statement.html',
{'problem': problem,
'statement_url': statement_url,
'can_admin_problem': can_admin_problem(request, problem)},
)
- return statement_html
+ return document_html
def check_for_statement(request, problem):
return ProblemStatement.objects.filter(problem=problem).exists()
@@ -128,7 +137,7 @@ def check_for_statement(request, problem):
)
def problem_site_statement(request, problem):
statement = query_statement(problem.id)
- return problem_site_document(request, problem, statement)
+ return problem_site_document(request, problem, statement, type='statement')
def show_editorial(request, problem):
@@ -139,7 +148,7 @@ def show_editorial(request, problem):
)
def problem_site_editorial(request, problem):
statement = query_editorial(problem.id)
- return problem_site_document(request, problem, statement)
+ return problem_site_document(request, problem, statement, type='editorial')
def check_for_downloads(request, problem):
From e5a0afe19001c8b42ebd3b2e191b7ea196e83c0e Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 18:58:36 +0200
Subject: [PATCH 09/29] Generalise problem_site_statement_zip_view into
problem_site_document_zip_view.
---
oioioi/problems/problem_site.py | 16 +++++++++++-----
oioioi/problems/urls.py | 7 +++++++
oioioi/problems/views.py | 4 ++--
3 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index a1cc78597..3eb44968b 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -81,12 +81,18 @@ def decorator(func):
return decorator
-def problem_site_statement_zip_view(request, site_key, path):
+def problem_site_document_zip_view(request, site_key, path, type='statement'):
problem = get_object_or_404(Problem, problemsite__url_key=site_key)
- statement = query_statement(problem.id)
- if not statement:
+
+ document = None
+ if type == 'editorial':
+ document = query_editorial(problem.id)
+ else:
+ document = query_statement(problem.id)
+
+ if not document:
raise Http404
- return query_zip(statement, path)
+ return query_zip(document, path)
def problem_site_document(request, problem, document, type):
@@ -106,7 +112,7 @@ def problem_site_document(request, problem, document, type):
else:
raise Http404("Document not found")
elif document.extension == '.zip':
- response = problem_site_statement_zip_view(
+ response = problem_site_document_zip_view(
request, problem.problemsite.url_key, 'index.html'
)
document_html = render_to_string(
diff --git a/oioioi/problems/urls.py b/oioioi/problems/urls.py
index d64d7579b..c420cc3af 100644
--- a/oioioi/problems/urls.py
+++ b/oioioi/problems/urls.py
@@ -7,9 +7,16 @@
problem_site_patterns = [
path('site/', views.problem_site_view, name='problem_site'),
+ path(
+ 'site/editorial/',
+ views.problem_site_document_zip_view,
+ {'type': 'editorial'},
+ name='problem_site_editorial_zip',
+ ),
path(
'site/',
views.problem_site_statement_zip_view,
+ {'type': 'statement'},
name='problem_site_statement_zip',
),
path(
diff --git a/oioioi/problems/views.py b/oioioi/problems/views.py
index e22e73012..6ed010326 100644
--- a/oioioi/problems/views.py
+++ b/oioioi/problems/views.py
@@ -72,12 +72,12 @@
UserStatistics,
)
-# problem_site_statement_zip_view is used in one of the tabs
+# problem_site_document_zip_view is used in some of the tabs
# in problem_site.py. We placed the view in problem_site.py
# instead of views.py to avoid circular imports. We still import
# it here to use it in urls.py.
from oioioi.problems.problem_site import (
- problem_site_statement_zip_view,
+ problem_site_document_zip_view,
problem_site_tab_registry,
)
from oioioi.problems.problem_sources import problem_sources
From 9f0f5310b8ec60a7e416e622b0a5314eece0423a Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 18:59:49 +0200
Subject: [PATCH 10/29] statement -> document in query_zip.
---
oioioi/problems/utils.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/oioioi/problems/utils.py b/oioioi/problems/utils.py
index 088e944cb..baf167dea 100644
--- a/oioioi/problems/utils.py
+++ b/oioioi/problems/utils.py
@@ -163,12 +163,12 @@ def query_editorial(problem_id):
return query_document(editorials)
-def query_zip(statement, path):
- if statement.extension != '.zip':
+def query_zip(document, path):
+ if document.extension != '.zip':
raise SuspiciousOperation
# ZipFile will call seek(), so we need a real file here
- zip = zipfile.ZipFile(statement.content.read_using_cache())
+ zip = zipfile.ZipFile(document.content.read_using_cache())
try:
info = zip.getinfo(path)
except KeyError:
From 24932ea9b469f6ddd6f160ce6327c3572d2ef91c Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:02:57 +0200
Subject: [PATCH 11/29] Rename from-zip-statemnt.html template to
from-zip-document.html.
---
oioioi/problems/problem_site.py | 2 +-
.../{from-zip-statement.html => from-zip-document.html} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename oioioi/problems/templates/problems/{from-zip-statement.html => from-zip-document.html} (100%)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 3eb44968b..8886a4ed6 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -116,7 +116,7 @@ def problem_site_document(request, problem, document, type):
request, problem.problemsite.url_key, 'index.html'
)
document_html = render_to_string(
- 'problems/from-zip-statement.html',
+ 'problems/from-zip-document.html',
{'problem': problem,
'statement': mark_safe(response.content.decode(errors="replace")),
'can_admin_problem': can_admin_problem(request, problem)}
diff --git a/oioioi/problems/templates/problems/from-zip-statement.html b/oioioi/problems/templates/problems/from-zip-document.html
similarity index 100%
rename from oioioi/problems/templates/problems/from-zip-statement.html
rename to oioioi/problems/templates/problems/from-zip-document.html
From 94ed2a07ba8cf246e1a324328d122ff27ce634f5 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:04:05 +0200
Subject: [PATCH 12/29] Pass type argument to problem_site_document_zip_view in
problem_site_document function.
---
oioioi/problems/problem_site.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 8886a4ed6..d10a7fa51 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -113,7 +113,7 @@ def problem_site_document(request, problem, document, type):
raise Http404("Document not found")
elif document.extension == '.zip':
response = problem_site_document_zip_view(
- request, problem.problemsite.url_key, 'index.html'
+ request, problem.problemsite.url_key, 'index.html', type
)
document_html = render_to_string(
'problems/from-zip-document.html',
From bedd34ce54ac5594fc7b751de8aff813e842e6f0 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:13:26 +0200
Subject: [PATCH 13/29] Generalise problem_site_external_statement_view ->
problem_site_external_statement_view .
---
oioioi/problems/urls.py | 9 ++++++++-
oioioi/problems/views.py | 17 ++++++++++++-----
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/oioioi/problems/urls.py b/oioioi/problems/urls.py
index c420cc3af..192a6cc7e 100644
--- a/oioioi/problems/urls.py
+++ b/oioioi/problems/urls.py
@@ -19,9 +19,16 @@
{'type': 'statement'},
name='problem_site_statement_zip',
),
+ path(
+ 'editorial/',
+ views.problem_site_external_document_view,
+ {'type': 'editorial'},
+ name='problem_site_external_editorial',
+ ),
path(
'statement/',
- views.problem_site_external_statement_view,
+ views.problem_site_external_document_view,
+ {'type': 'statement'},
name='problem_site_external_statement',
),
path(
diff --git a/oioioi/problems/views.py b/oioioi/problems/views.py
index 6ed010326..cfa402b0d 100644
--- a/oioioi/problems/views.py
+++ b/oioioi/problems/views.py
@@ -90,6 +90,7 @@
generate_add_to_contest_metadata,
generate_model_solutions_context,
get_prefetched_value,
+ query_editorial,
query_statement,
show_proposal_form,
)
@@ -615,14 +616,20 @@ def build_link(tab):
)
-def problem_site_external_statement_view(request, site_key):
+def problem_site_external_document_view(request, site_key, type='statement'):
problem = get_object_or_404(Problem, problemsite__url_key=site_key)
- statement = query_statement(problem.id)
- if not statement:
+
+ document = None
+ if type == 'editorial':
+ document = query_editorial(problem.id)
+ elif type == 'statement':
+ document = query_statement(problem.id)
+
+ if not document:
raise Http404
- if statement.extension == '.zip' and not can_admin_problem(request, problem):
+ if document.extension == '.zip' and not can_admin_problem(request, problem):
raise PermissionDenied
- return stream_file(statement.content, statement.download_name)
+ return stream_file(document.content, document.download_name)
def problem_site_external_attachment_view(request, site_key, attachment_id):
From 520f6879f98b9ac30588f8300e575ec7c9ef9835 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:14:31 +0200
Subject: [PATCH 14/29] Make problem_site_document_zip_view throw Http404 for
unknown document types.
---
oioioi/problems/problem_site.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index d10a7fa51..389bfa96b 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -87,7 +87,7 @@ def problem_site_document_zip_view(request, site_key, path, type='statement'):
document = None
if type == 'editorial':
document = query_editorial(problem.id)
- else:
+ elif type == 'statement':
document = query_statement(problem.id)
if not document:
From 010e8fe683683e2631a50d7eb2429dd96dadc03e Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:16:40 +0200
Subject: [PATCH 15/29] Correct document_url setting for
external-statement.html page.
---
oioioi/problems/problem_site.py | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 389bfa96b..6c34d987d 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -122,14 +122,24 @@ def problem_site_document(request, problem, document, type):
'can_admin_problem': can_admin_problem(request, problem)}
)
else:
- statement_url = reverse(
- 'problem_site_external_statement',
- kwargs={'site_key': problem.problemsite.url_key},
- )
+ document_url = None
+ if type == 'editorial':
+ document_url = reverse(
+ 'problem_site_external_editorial',
+ kwargs={'site_key': problem.problemsite.url_key},
+ )
+ elif type == 'statement':
+ document_url = reverse(
+ 'problem_site_external_statement',
+ kwargs={'site_key': problem.problemsite.url_key},
+ )
+ else:
+ raise Http404("Document not found")
+
document_html = render_to_string(
'problems/external-statement.html',
{'problem': problem,
- 'statement_url': statement_url,
+ 'statement_url': document_url,
'can_admin_problem': can_admin_problem(request, problem)},
)
From 840d0dc2a6588120c3651c69f31340014b2b6315 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:17:58 +0200
Subject: [PATCH 16/29] Rename external-statement.html ->
external-document.html.
---
oioioi/problems/problem_site.py | 2 +-
.../{external-statement.html => external-document.html} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename oioioi/problems/templates/problems/{external-statement.html => external-document.html} (100%)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 6c34d987d..90add8850 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -137,7 +137,7 @@ def problem_site_document(request, problem, document, type):
raise Http404("Document not found")
document_html = render_to_string(
- 'problems/external-statement.html',
+ 'problems/external-document.html',
{'problem': problem,
'statement_url': document_url,
'can_admin_problem': can_admin_problem(request, problem)},
diff --git a/oioioi/problems/templates/problems/external-statement.html b/oioioi/problems/templates/problems/external-document.html
similarity index 100%
rename from oioioi/problems/templates/problems/external-statement.html
rename to oioioi/problems/templates/problems/external-document.html
From 816fe54e903889567651d648c156aeeee6bb9101 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:23:23 +0200
Subject: [PATCH 17/29] Generalise external-document.html contents.
---
oioioi/problems/problem_site.py | 3 ++-
.../problems/templates/problems/external-document.html | 10 ++++++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 90add8850..8a510b07c 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -139,7 +139,8 @@ def problem_site_document(request, problem, document, type):
document_html = render_to_string(
'problems/external-document.html',
{'problem': problem,
- 'statement_url': document_url,
+ 'document_url': document_url,
+ 'document_type': type,
'can_admin_problem': can_admin_problem(request, problem)},
)
diff --git a/oioioi/problems/templates/problems/external-document.html b/oioioi/problems/templates/problems/external-document.html
index d4a92954f..1d2b9c6d2 100644
--- a/oioioi/problems/templates/problems/external-document.html
+++ b/oioioi/problems/templates/problems/external-document.html
@@ -1,10 +1,16 @@
{% load i18n %}
-
+
- {% blocktrans %}You can also open the problem's statement by clicking
here .{% endblocktrans %}
+ {% if document_type == 'statement' %}
+ {% blocktrans %}You can also open the problem's statement by clicking
here .{% endblocktrans %}
+ {% elif document_type == 'editorial' %}
+ {% blocktrans %}You can also open the problem's editorial by clicking
here .{% endblocktrans %}
+ {% else %}
+ {% blocktrans %}You can also open this document by clicking
here .{% endblocktrans %}
+ {% endif %}
{% if can_admin_problem %}
From 554d851c7189e0340db1b0e11857fbb2a8df8be2 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:27:56 +0200
Subject: [PATCH 18/29] Wording change.
---
oioioi/problems/templates/problems/no-problem-editorial.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/oioioi/problems/templates/problems/no-problem-editorial.html b/oioioi/problems/templates/problems/no-problem-editorial.html
index dca8ec537..bac1e586b 100644
--- a/oioioi/problems/templates/problems/no-problem-editorial.html
+++ b/oioioi/problems/templates/problems/no-problem-editorial.html
@@ -2,7 +2,7 @@
{% blocktrans %}
-
This problem doesn't have a publicly available editorial.
+
This problem doesn't have any editorial available .
{% endblocktrans %}
From a62366429312fadd452d6a0a3040a43e1f403662 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:31:26 +0200
Subject: [PATCH 19/29] Consistent case ordering in problem_site_document.
---
oioioi/problems/problem_site.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 8a510b07c..1e77b2299 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -123,14 +123,14 @@ def problem_site_document(request, problem, document, type):
)
else:
document_url = None
- if type == 'editorial':
+ if type == 'statement':
document_url = reverse(
- 'problem_site_external_editorial',
+ 'problem_site_external_statement',
kwargs={'site_key': problem.problemsite.url_key},
)
- elif type == 'statement':
+ elif type == 'editorial':
document_url = reverse(
- 'problem_site_external_statement',
+ 'problem_site_external_editorial',
kwargs={'site_key': problem.problemsite.url_key},
)
else:
From 6fda6e820e2ccbf7a84904f249389507d268fe93 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:32:54 +0200
Subject: [PATCH 20/29] Problem statement views before editorial views.
---
oioioi/problems/urls.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/oioioi/problems/urls.py b/oioioi/problems/urls.py
index 192a6cc7e..e0b63bac5 100644
--- a/oioioi/problems/urls.py
+++ b/oioioi/problems/urls.py
@@ -7,12 +7,6 @@
problem_site_patterns = [
path('site/', views.problem_site_view, name='problem_site'),
- path(
- 'site/editorial/',
- views.problem_site_document_zip_view,
- {'type': 'editorial'},
- name='problem_site_editorial_zip',
- ),
path(
'site/',
views.problem_site_statement_zip_view,
@@ -20,10 +14,10 @@
name='problem_site_statement_zip',
),
path(
- 'editorial/',
- views.problem_site_external_document_view,
+ 'site/editorial/',
+ views.problem_site_document_zip_view,
{'type': 'editorial'},
- name='problem_site_external_editorial',
+ name='problem_site_editorial_zip',
),
path(
'statement/',
@@ -31,6 +25,12 @@
{'type': 'statement'},
name='problem_site_external_statement',
),
+ path(
+ 'editorial/',
+ views.problem_site_external_document_view,
+ {'type': 'editorial'},
+ name='problem_site_external_editorial',
+ ),
path(
'attachment//',
views.problem_site_external_attachment_view,
From 1309e1d5d7747106b504a276b4020c0628688b54 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Wed, 28 May 2025 19:33:36 +0200
Subject: [PATCH 21/29] Fix view for pattern problem_site_statement_zip.
---
oioioi/problems/urls.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/oioioi/problems/urls.py b/oioioi/problems/urls.py
index e0b63bac5..d560aea52 100644
--- a/oioioi/problems/urls.py
+++ b/oioioi/problems/urls.py
@@ -9,7 +9,7 @@
path('site/', views.problem_site_view, name='problem_site'),
path(
'site/',
- views.problem_site_statement_zip_view,
+ views.problem_site_document_zip_view,
{'type': 'statement'},
name='problem_site_statement_zip',
),
From a2afaf4075194cc6f26c35ee23bb438a305b43e4 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Thu, 5 Jun 2025 11:26:25 +0200
Subject: [PATCH 22/29] Rename replace_problem_statement tab to
replace_statement_or_editorial.
---
oioioi/contests/admin.py | 2 +-
oioioi/problems/problem_site.py | 6 +++---
oioioi/problems/tests/test_problem.py | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/oioioi/contests/admin.py b/oioioi/contests/admin.py
index c6060d08d..00aa9b7e2 100644
--- a/oioioi/contests/admin.py
+++ b/oioioi/contests/admin.py
@@ -468,7 +468,7 @@ def _replace_statement_href(self, instance):
return (
reverse('problem_site', args=(instance.problem.problemsite.url_key,))
+ '?'
- + urllib.parse.urlencode({'key': 'replace_problem_statement'})
+ + urllib.parse.urlencode({'key': 'replace_statement_or_editorial'})
)
def _package_manage_href(self, instance):
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index 1e77b2299..d1b835da8 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -349,8 +349,8 @@ def problem_site_add_to_contest(request, problem):
@problem_site_tab(
- _("Replace problem statement"),
- key='replace_problem_statement',
+ _("Replace statement or editorial"),
+ key='replace_statement_or_editorial',
order=800,
condition=can_admin_problem,
)
@@ -371,7 +371,7 @@ def problem_site_replace_statement(request, problem):
url = reverse(
'problem_site', kwargs={'site_key': problem.problemsite.url_key}
)
- return redirect(url + '?key=replace_problem_statement')
+ return redirect(url + '?key=replace_statement_or_editorial')
else:
form.add_error(None, _("Picked statement file does not exist."))
else:
diff --git a/oioioi/problems/tests/test_problem.py b/oioioi/problems/tests/test_problem.py
index c799d805d..892203395 100644
--- a/oioioi/problems/tests/test_problem.py
+++ b/oioioi/problems/tests/test_problem.py
@@ -476,7 +476,7 @@ def test_tags_tab_user_with_permission(self):
def test_statement_replacement(self):
url = (
reverse('problem_site', kwargs={'site_key': '123'})
- + '?key=replace_problem_statement'
+ + '?key=replace_statement_or_editorial'
)
self.assertTrue(self.client.login(username='test_user'))
From 870d8785781eb6b3ff3a2105e40d40485bc2d85b Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Tue, 10 Jun 2025 14:51:51 +0200
Subject: [PATCH 23/29] Update problem_site_replace_statement ->
problem_site_replace_statement_or_editorial.
---
oioioi/problems/problem_site.py | 53 ++++++++++++++++++++-------------
1 file changed, 33 insertions(+), 20 deletions(-)
diff --git a/oioioi/problems/problem_site.py b/oioioi/problems/problem_site.py
index d1b835da8..b287f79f7 100644
--- a/oioioi/problems/problem_site.py
+++ b/oioioi/problems/problem_site.py
@@ -354,32 +354,45 @@ def problem_site_add_to_contest(request, problem):
order=800,
condition=can_admin_problem,
)
-def problem_site_replace_statement(request, problem):
- statements = ProblemStatement.objects.filter(problem=problem)
- filenames = [statement.filename for statement in statements]
+def problem_site_replace_statement_or_editorial(request, problem):
+ statements = ProblemStatement .objects.filter(problem=problem)
+ editorials = ProblemEditorial.objects.filter(problem=problem)
+
+ stmt_names = [s.filename for s in statements]
+ ed_names = [e.filename for e in editorials]
+
+ stmt_form = ProblemStatementReplaceForm(stmt_names)
+ ed_form = ProblemStatementReplaceForm(ed_names)
if request.method == 'POST':
- form = PackageFileReuploadForm(filenames, request.POST, request.FILES)
- if form.is_valid():
- statement_filename = form.cleaned_data['file_name']
- statements = [s for s in statements if s.filename == statement_filename]
- if statements:
- statement = statements[0]
- new_statement_file = form.cleaned_data['file_replacement']
- statement.content = new_statement_file
- statement.save()
- url = reverse(
- 'problem_site', kwargs={'site_key': problem.problemsite.url_key}
- )
+ form_type = request.POST.get('form_type')
+ if form_type == 'statement':
+ stmt_form = ProblemStatementReplaceForm(stmt_names, request.POST, request.FILES)
+ if stmt_form.is_valid():
+ fn = stmt_form.cleaned_data['file_name']
+ stmt = next(s for s in statements if s.filename == fn)
+ stmt.content = stmt_form.cleaned_data['file_replacement']
+ stmt.save()
+ url = reverse('problem_site', kwargs={'site_key': problem.problemsite.url_key})
return redirect(url + '?key=replace_statement_or_editorial')
- else:
- form.add_error(None, _("Picked statement file does not exist."))
- else:
- form = ProblemStatementReplaceForm(filenames)
+ elif form_type == 'editorial':
+ ed_form = ProblemStatementReplaceForm(ed_names, request.POST, request.FILES)
+ if ed_form.is_valid():
+ fn = ed_form.cleaned_data['file_name']
+ ed = next(e for e in editorials if e.filename == fn)
+ ed.content = ed_form.cleaned_data['file_replacement']
+ ed.save()
+ url = reverse('problem_site', kwargs={'site_key': problem.problemsite.url_key})
+ return redirect(url + '?key=replace_statement_or_editorial')
+
return TemplateResponse(
request,
'problems/replace-problem-statement.html',
- {'form': form, 'problem': problem},
+ {
+ 'problem': problem,
+ 'statement_form': stmt_form,
+ 'editorial_form': ed_form,
+ },
)
From ccc5ef6a2eb2cbc11246ce9988b6ef13c87ee4c0 Mon Sep 17 00:00:00 2001
From: Grzegorz Krawczyk
Date: Tue, 10 Jun 2025 14:57:41 +0200
Subject: [PATCH 24/29] replace-problem-statement.html with two identical
forms, one for statement, other for editorial.
---
.../problems/replace-problem-statement.html | 92 ++++++++++++-------
1 file changed, 59 insertions(+), 33 deletions(-)
diff --git a/oioioi/problems/templates/problems/replace-problem-statement.html b/oioioi/problems/templates/problems/replace-problem-statement.html
index bf1a99356..1f7db33ca 100644
--- a/oioioi/problems/templates/problems/replace-problem-statement.html
+++ b/oioioi/problems/templates/problems/replace-problem-statement.html
@@ -10,54 +10,80 @@
{% trans "Manage package files" %}.
+
+{% trans "Replace statement" %}
+
+{% trans "Replace editorial" %}
+