Add pdf option for problem
This commit is contained in:
parent
98b8cbe518
commit
69f08e84b5
7 changed files with 65 additions and 3 deletions
|
@ -256,6 +256,7 @@ urlpatterns = [
|
||||||
problem.ProblemPdfView.as_view(),
|
problem.ProblemPdfView.as_view(),
|
||||||
name="problem_pdf",
|
name="problem_pdf",
|
||||||
),
|
),
|
||||||
|
url(r"^/pdf_description$", problem.ProblemPdfDescriptionView.as_view(), name="problem_pdf_description"),
|
||||||
url(r"^/clone", problem.ProblemClone.as_view(), name="problem_clone"),
|
url(r"^/clone", problem.ProblemClone.as_view(), name="problem_clone"),
|
||||||
url(r"^/submit$", problem.problem_submit, name="problem_submit"),
|
url(r"^/submit$", problem.problem_submit, name="problem_submit"),
|
||||||
url(
|
url(
|
||||||
|
|
|
@ -193,6 +193,7 @@ class ProblemAdmin(CompareVersionAdmin):
|
||||||
"is_organization_private",
|
"is_organization_private",
|
||||||
"organizations",
|
"organizations",
|
||||||
"description",
|
"description",
|
||||||
|
"pdf_description",
|
||||||
"license",
|
"license",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
25
judge/migrations/0130_auto_20220831_1048.py
Normal file
25
judge/migrations/0130_auto_20220831_1048.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 2.2.25 on 2022-08-31 03:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import judge.models.problem
|
||||||
|
import judge.utils.problem_data
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('judge', '0129_auto_20220622_1424'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='problem',
|
||||||
|
name='pdf_description',
|
||||||
|
field=models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem.problem_directory_file, verbose_name='pdf statement'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='problem',
|
||||||
|
name='description',
|
||||||
|
field=models.TextField(blank=True, verbose_name='problem body'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -18,6 +18,7 @@ from judge.models.profile import Organization, Profile
|
||||||
from judge.models.runtime import Language
|
from judge.models.runtime import Language
|
||||||
from judge.user_translations import gettext as user_gettext
|
from judge.user_translations import gettext as user_gettext
|
||||||
from judge.utils.raw_sql import RawSQLColumn, unique_together_left_join
|
from judge.utils.raw_sql import RawSQLColumn, unique_together_left_join
|
||||||
|
from judge.models.problem_data import problem_data_storage, problem_directory_file_helper
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"ProblemGroup",
|
"ProblemGroup",
|
||||||
|
@ -31,6 +32,8 @@ __all__ = [
|
||||||
"TranslatedProblemForeignKeyQuerySet",
|
"TranslatedProblemForeignKeyQuerySet",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def problem_directory_file(data, filename):
|
||||||
|
return problem_directory_file_helper(data.code, filename)
|
||||||
|
|
||||||
class ProblemType(models.Model):
|
class ProblemType(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
|
@ -160,7 +163,7 @@ class Problem(models.Model):
|
||||||
db_index=True,
|
db_index=True,
|
||||||
help_text=_("The full name of the problem, " "as shown in the problem list."),
|
help_text=_("The full name of the problem, " "as shown in the problem list."),
|
||||||
)
|
)
|
||||||
description = models.TextField(verbose_name=_("problem body"))
|
description = models.TextField(verbose_name=_("problem body"), blank=True)
|
||||||
authors = models.ManyToManyField(
|
authors = models.ManyToManyField(
|
||||||
Profile,
|
Profile,
|
||||||
verbose_name=_("creators"),
|
verbose_name=_("creators"),
|
||||||
|
@ -299,6 +302,13 @@ class Problem(models.Model):
|
||||||
is_organization_private = models.BooleanField(
|
is_organization_private = models.BooleanField(
|
||||||
verbose_name=_("private to organizations"), default=False
|
verbose_name=_("private to organizations"), default=False
|
||||||
)
|
)
|
||||||
|
pdf_description = models.FileField(
|
||||||
|
verbose_name=_("pdf statement"),
|
||||||
|
storage=problem_data_storage,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
upload_to=problem_directory_file,
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Problem, self).__init__(*args, **kwargs)
|
super(Problem, self).__init__(*args, **kwargs)
|
||||||
|
|
|
@ -20,12 +20,12 @@ __all__ = [
|
||||||
problem_data_storage = ProblemDataStorage()
|
problem_data_storage = ProblemDataStorage()
|
||||||
|
|
||||||
|
|
||||||
def _problem_directory_file(code, filename):
|
def problem_directory_file_helper(code, filename):
|
||||||
return os.path.join(code, os.path.basename(filename))
|
return os.path.join(code, os.path.basename(filename))
|
||||||
|
|
||||||
|
|
||||||
def problem_directory_file(data, filename):
|
def problem_directory_file(data, filename):
|
||||||
return _problem_directory_file(data.problem.code, filename)
|
return problem_directory_file_helper(data.problem.code, filename)
|
||||||
|
|
||||||
|
|
||||||
CHECKERS = (
|
CHECKERS = (
|
||||||
|
|
|
@ -402,6 +402,27 @@ class ProblemPdfView(ProblemMixin, SingleObjectMixin, View):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class ProblemPdfDescriptionView(ProblemMixin, SingleObjectMixin, View):
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
problem = self.get_object()
|
||||||
|
if not problem.pdf_description:
|
||||||
|
raise Http404()
|
||||||
|
response = HttpResponse()
|
||||||
|
if hasattr(settings, "DMOJ_PDF_PROBLEM_INTERNAL") and request.META.get(
|
||||||
|
"SERVER_SOFTWARE", ""
|
||||||
|
).startswith("nginx/"):
|
||||||
|
response["X-Accel-Redirect"] = problem.pdf_description.path
|
||||||
|
else:
|
||||||
|
with open(problem.pdf_description.path, "rb") as f:
|
||||||
|
response.content = f.read()
|
||||||
|
|
||||||
|
response["Content-Type"] = "application/pdf"
|
||||||
|
response["Content-Disposition"] = "inline; filename=%s.pdf" % (
|
||||||
|
problem.code,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView):
|
class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView):
|
||||||
model = Problem
|
model = Problem
|
||||||
title = gettext_lazy("Problems")
|
title = gettext_lazy("Problems")
|
||||||
|
|
|
@ -377,6 +377,10 @@
|
||||||
{{ description|markdown("problem", MATH_ENGINE)|reference|str|safe }}
|
{{ description|markdown("problem", MATH_ENGINE)|reference|str|safe }}
|
||||||
{% endcache %}
|
{% endcache %}
|
||||||
|
|
||||||
|
{% if problem.pdf_description %}
|
||||||
|
<embed src="{{url('problem_pdf_description', problem.code)}}" width="100%" height="500" type="application/pdf">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% with license=problem.license %}
|
{% with license=problem.license %}
|
||||||
{% if license %}
|
{% if license %}
|
||||||
<span class="license">
|
<span class="license">
|
||||||
|
|
Loading…
Reference in a new issue