Move problem clarification to contest clarification

This commit is contained in:
cuom1999 2022-10-12 21:19:22 -05:00
parent f001ae4e0a
commit 0ee7de1b46
11 changed files with 115 additions and 79 deletions

View file

@ -20,7 +20,6 @@ from judge.models import (
LanguageLimit, LanguageLimit,
LanguageTemplate, LanguageTemplate,
Problem, Problem,
ProblemClarification,
ProblemTranslation, ProblemTranslation,
Profile, Profile,
Solution, Solution,
@ -147,23 +146,6 @@ class LanguageTemplateInline(admin.TabularInline):
form = LanguageTemplateInlineForm form = LanguageTemplateInlineForm
class ProblemClarificationForm(ModelForm):
class Meta:
if HeavyPreviewPageDownWidget is not None:
widgets = {
"description": HeavyPreviewPageDownWidget(
preview=reverse_lazy("comment_preview")
)
}
class ProblemClarificationInline(admin.StackedInline):
model = ProblemClarification
fields = ("description",)
form = ProblemClarificationForm
extra = 0
class ProblemSolutionForm(ModelForm): class ProblemSolutionForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ProblemSolutionForm, self).__init__(*args, **kwargs) super(ProblemSolutionForm, self).__init__(*args, **kwargs)
@ -256,7 +238,6 @@ class ProblemAdmin(CompareVersionAdmin):
inlines = [ inlines = [
LanguageLimitInline, LanguageLimitInline,
LanguageTemplateInline, LanguageTemplateInline,
ProblemClarificationInline,
ProblemSolutionInline, ProblemSolutionInline,
ProblemTranslationInline, ProblemTranslationInline,
] ]

View file

@ -0,0 +1,46 @@
# Generated by Django 2.2.25 on 2022-10-13 01:50
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("judge", "0132_auto_20220915_1349"),
]
operations = [
migrations.CreateModel(
name="ContestProblemClarification",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("description", models.TextField(verbose_name="clarification body")),
(
"date",
models.DateTimeField(
auto_now_add=True, verbose_name="clarification timestamp"
),
),
(
"problem",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="judge.ContestProblem",
verbose_name="clarified problem",
),
),
],
),
migrations.DeleteModel(
name="ProblemClarification",
),
]

View file

@ -15,6 +15,7 @@ from judge.models.contest import (
ContestSubmission, ContestSubmission,
ContestTag, ContestTag,
Rating, Rating,
ContestProblemClarification,
) )
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
@ -23,7 +24,6 @@ from judge.models.problem import (
LanguageTemplate, LanguageTemplate,
License, License,
Problem, Problem,
ProblemClarification,
ProblemGroup, ProblemGroup,
ProblemTranslation, ProblemTranslation,
ProblemType, ProblemType,

View file

@ -29,6 +29,7 @@ __all__ = [
"ContestProblem", "ContestProblem",
"ContestSubmission", "ContestSubmission",
"Rating", "Rating",
"ContestProblemClarification",
] ]
@ -766,6 +767,10 @@ class ContestProblem(models.Model):
], ],
) )
@property
def clarifications(self):
return ContestProblemClarification.objects.filter(problem=self)
class Meta: class Meta:
unique_together = ("problem", "contest") unique_together = ("problem", "contest")
verbose_name = _("contest problem") verbose_name = _("contest problem")
@ -853,3 +858,13 @@ class ContestMoss(models.Model):
unique_together = ("contest", "problem", "language") unique_together = ("contest", "problem", "language")
verbose_name = _("contest moss result") verbose_name = _("contest moss result")
verbose_name_plural = _("contest moss results") verbose_name_plural = _("contest moss results")
class ContestProblemClarification(models.Model):
problem = models.ForeignKey(
ContestProblem, verbose_name=_("clarified problem"), on_delete=CASCADE
)
description = models.TextField(verbose_name=_("clarification body"))
date = models.DateTimeField(
verbose_name=_("clarification timestamp"), auto_now_add=True
)

View file

@ -28,7 +28,6 @@ __all__ = [
"ProblemType", "ProblemType",
"Problem", "Problem",
"ProblemTranslation", "ProblemTranslation",
"ProblemClarification",
"License", "License",
"Solution", "Solution",
"TranslatedProblemQuerySet", "TranslatedProblemQuerySet",
@ -490,10 +489,6 @@ class Problem(models.Model):
def i18n_name(self, value): def i18n_name(self, value):
self._i18n_name = value self._i18n_name = value
@property
def clarifications(self):
return ProblemClarification.objects.filter(problem=self)
def update_stats(self): def update_stats(self):
self.user_count = ( self.user_count = (
self.submission_set.filter( self.submission_set.filter(
@ -613,16 +608,6 @@ class ProblemTranslation(models.Model):
verbose_name_plural = _("problem translations") verbose_name_plural = _("problem translations")
class ProblemClarification(models.Model):
problem = models.ForeignKey(
Problem, verbose_name=_("clarified problem"), on_delete=CASCADE
)
description = models.TextField(verbose_name=_("clarification body"))
date = models.DateTimeField(
verbose_name=_("clarification timestamp"), auto_now_add=True
)
class LanguageLimit(models.Model): class LanguageLimit(models.Model):
problem = models.ForeignKey( problem = models.ForeignKey(
Problem, Problem,

View file

@ -14,7 +14,7 @@ from judge.models import (
Contest, Contest,
Language, Language,
Problem, Problem,
ProblemClarification, ContestProblemClarification,
Profile, Profile,
Submission, Submission,
Ticket, Ticket,
@ -50,8 +50,8 @@ class FeedView(ListView):
if self.request.user.is_authenticated: if self.request.user.is_authenticated:
participation = self.request.profile.current_contest participation = self.request.profile.current_contest
if participation: if participation:
clarifications = ProblemClarification.objects.filter( clarifications = ContestProblemClarification.objects.filter(
problem__in=participation.contest.problems.all() problem__in=participation.contest.contest_problems.all()
) )
context["has_clarifications"] = clarifications.count() > 0 context["has_clarifications"] = clarifications.count() > 0
context["clarifications"] = clarifications.order_by("-date") context["clarifications"] = clarifications.order_by("-date")

View file

@ -65,7 +65,7 @@ from judge.models import (
Problem, Problem,
Profile, Profile,
Submission, Submission,
ProblemClarification, ContestProblemClarification,
) )
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
@ -1179,7 +1179,7 @@ class ContestTagDetail(TitleMixin, ContestTagDetailAjax):
return _("Contest tag: %s") % self.object.name return _("Contest tag: %s") % self.object.name
class ProblemClarificationForm(forms.Form): class ContestProblemClarificationForm(forms.Form):
body = forms.CharField( body = forms.CharField(
widget=HeavyPreviewPageDownWidget( widget=HeavyPreviewPageDownWidget(
preview=reverse_lazy("comment_preview"), preview=reverse_lazy("comment_preview"),
@ -1190,12 +1190,12 @@ class ProblemClarificationForm(forms.Form):
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
self.request = request self.request = request
super(ProblemClarificationForm, self).__init__(*args, **kwargs) super(ContestProblemClarificationForm, self).__init__(*args, **kwargs)
self.fields["body"].widget.attrs.update({"placeholder": _("Issue description")}) self.fields["body"].widget.attrs.update({"placeholder": _("Issue description")})
class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView): class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView):
form_class = ProblemClarificationForm form_class = ContestProblemClarificationForm
template_name = "contest/clarification.html" template_name = "contest/clarification.html"
def get_form_kwargs(self): def get_form_kwargs(self):
@ -1225,12 +1225,13 @@ class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView
problem_code = self.request.POST["problem"] problem_code = self.request.POST["problem"]
description = form.cleaned_data["body"] description = form.cleaned_data["body"]
clarification = ProblemClarification(description=description) clarification = ContestProblemClarification(description=description)
clarification.problem = Problem.objects.get(code=problem_code) clarification.problem = get_object_or_404(
ContestProblem, contest=self.get_object(), problem__code=problem_code
)
clarification.save() clarification.save()
link = reverse("home") return HttpResponseRedirect(reverse("problem_list"))
return HttpResponseRedirect(link)
def get_title(self): def get_title(self):
return "New clarification for %s" % self.object.name return "New clarification for %s" % self.object.name
@ -1264,26 +1265,27 @@ class ContestClarificationAjax(ContestMixin, DetailView):
minutes=polling_time minutes=polling_time
) )
queryset = list( queryset = ContestProblemClarification.objects.filter(
ProblemClarification.objects.filter( problem__in=self.object.contest_problems.all(), date__gte=last_one_minute
problem__in=self.object.problems.all(), date__gte=last_one_minute
).values("problem", "problem__name", "description")
) )
problems = list( problems = list(
ContestProblem.objects.filter(contest=self.object) ContestProblem.objects.filter(contest=self.object)
.order_by("order") .order_by("order")
.values("problem") .values_list("problem__code", flat=True)
) )
problems = [i["problem"] for i in problems] res = []
for cla in queryset: for clarification in queryset:
cla["order"] = self.object.get_label_for_problem( value = {
problems.index(cla["problem"]) "order": self.object.get_label_for_problem(
) problems.index(clarification.problem.problem.code)
),
"problem__name": clarification.problem.problem.name,
"description": clarification.description,
}
res.append(value)
return JsonResponse( return JsonResponse(res, safe=False, json_dumps_params={"ensure_ascii": False})
queryset, safe=False, json_dumps_params={"ensure_ascii": False}
)
def update_contest_mode(request): def update_contest_mode(request):

View file

@ -930,7 +930,12 @@ class EditOrganizationContest(
for problem_form in problem_formset.deleted_objects: for problem_form in problem_formset.deleted_objects:
problem_form.delete() problem_form.delete()
super().post(request, *args, **kwargs) super().post(request, *args, **kwargs)
return HttpResponseRedirect(reverse("organization_contests", args=(self.organization_id,self.organization.slug))) return HttpResponseRedirect(
reverse(
"organization_contests",
args=(self.organization_id, self.organization.slug),
)
)
self.object = self.contest self.object = self.contest
return self.render_to_response( return self.render_to_response(
@ -1062,9 +1067,9 @@ class EditOrganizationBlog(
_("Not allowed to edit this blog"), _("Not allowed to edit this blog"),
) )
def delete_blog(self , request , *args , **kwargs): def delete_blog(self, request, *args, **kwargs):
self.blog_id = kwargs["blog_pk"] self.blog_id = kwargs["blog_pk"]
BlogPost.objects.get(pk = self.blog_id).delete() BlogPost.objects.get(pk=self.blog_id).delete()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
res = self.setup_blog(request, *args, **kwargs) res = self.setup_blog(request, *args, **kwargs)
@ -1076,10 +1081,13 @@ class EditOrganizationBlog(
res = self.setup_blog(request, *args, **kwargs) res = self.setup_blog(request, *args, **kwargs)
if res: if res:
return res return res
if request.POST['action'] == 'Delete': if request.POST["action"] == "Delete":
self.create_notification("Delete blog") self.create_notification("Delete blog")
self.delete_blog(request , *args , **kwargs) self.delete_blog(request, *args, **kwargs)
cur_url = reverse("organization_pending_blogs", args=(self.organization_id,self.organization.slug) ) cur_url = reverse(
"organization_pending_blogs",
args=(self.organization_id, self.organization.slug),
)
return HttpResponseRedirect(cur_url) return HttpResponseRedirect(cur_url)
else: else:
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)
@ -1090,15 +1098,13 @@ class EditOrganizationBlog(
def get_title(self): def get_title(self):
return _("Edit blog %s") % self.object.title return _("Edit blog %s") % self.object.title
def create_notification(self,action): def create_notification(self, action):
blog = BlogPost.objects.get(pk=self.blog_id) blog = BlogPost.objects.get(pk=self.blog_id)
link = reverse( link = reverse(
"edit_organization_blog", "edit_organization_blog",
args=[self.organization.id, self.organization.slug, self.blog_id], args=[self.organization.id, self.organization.slug, self.blog_id],
) )
html = ( html = f'<a href="{link}">{blog.title} - {self.organization.name}</a>'
f'<a href="{link}">{blog.title} - {self.organization.name}</a>'
)
post_authors = blog.authors.all() post_authors = blog.authors.all()
posible_user = self.organization.admins.all() | post_authors posible_user = self.organization.admins.all() | post_authors
for user in posible_user: for user in posible_user:
@ -1107,11 +1113,11 @@ class EditOrganizationBlog(
notification = Notification( notification = Notification(
owner=user, owner=user,
author=self.request.profile, author=self.request.profile,
category= action, category=action,
html_link=html, html_link=html,
) )
notification.save() notification.save()
def form_valid(self, form): def form_valid(self, form):
with transaction.atomic(), revisions.create_revision(): with transaction.atomic(), revisions.create_revision():
res = super(EditOrganizationBlog, self).form_valid(form) res = super(EditOrganizationBlog, self).form_valid(form)
@ -1120,6 +1126,7 @@ class EditOrganizationBlog(
self.create_notification("Edit blog") self.create_notification("Edit blog")
return res return res
class PendingBlogs( class PendingBlogs(
LoginRequiredMixin, LoginRequiredMixin,
TitleMixin, TitleMixin,

View file

@ -40,7 +40,7 @@ from judge.models import (
Judge, Judge,
Language, Language,
Problem, Problem,
ProblemClarification, ContestProblemClarification,
ProblemGroup, ProblemGroup,
ProblemTranslation, ProblemTranslation,
ProblemType, ProblemType,
@ -251,7 +251,7 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView):
context["contest_problem"] = contest_problem context["contest_problem"] = contest_problem
if contest_problem: if contest_problem:
clarifications = self.object.clarifications clarifications = contest_problem.clarifications
context["has_clarifications"] = clarifications.count() > 0 context["has_clarifications"] = clarifications.count() > 0
context["clarifications"] = clarifications.order_by("-date") context["clarifications"] = clarifications.order_by("-date")
context["submission_limit"] = contest_problem.max_submissions context["submission_limit"] = contest_problem.max_submissions
@ -665,8 +665,8 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
if self.request.user.is_authenticated: if self.request.user.is_authenticated:
participation = self.request.profile.current_contest participation = self.request.profile.current_contest
if participation: if participation:
clarifications = ProblemClarification.objects.filter( clarifications = ContestProblemClarification.objects.filter(
problem__in=participation.contest.problems.all() problem__in=participation.contest.contest_problems.all()
) )
context["has_clarifications"] = clarifications.count() > 0 context["has_clarifications"] = clarifications.count() > 0
context["clarifications"] = clarifications.order_by("-date") context["clarifications"] = clarifications.order_by("-date")

View file

@ -106,9 +106,9 @@
<ul> <ul>
{% for clarification in clarifications %} {% for clarification in clarifications %}
<li class="clarification"> <li class="clarification">
<a href="{{ url('problem_detail', clarification.problem.code) }}" <a href="{{ url('problem_detail', clarification.problem.problem.code) }}"
class="problem"> class="problem">
{{ clarification.problem.name }} {{ clarification.problem.problem.name }}
</a> </a>
<span class="time">{{ relative_time(clarification.date) }}</span> <span class="time">{{ relative_time(clarification.date) }}</span>
</li> </li>

View file

@ -150,9 +150,9 @@
{% for clarification in clarifications %} {% for clarification in clarifications %}
<tr> <tr>
<td> <td>
<a href="{{ url('problem_detail', clarification.problem.code) }}" <a href="{{ url('problem_detail', clarification.problem.problem.code) }}"
class="problem"> class="problem">
{{ clarification.problem.name }} {{ clarification.problem.problem.name }}
</a> </a>
</td> </td>
<td class="time">{{ relative_time(clarification.date) }}</td> <td class="time">{{ relative_time(clarification.date) }}</td>