diff --git a/judge/admin/problem.py b/judge/admin/problem.py index 4c0e9f5..4f70ed4 100644 --- a/judge/admin/problem.py +++ b/judge/admin/problem.py @@ -20,7 +20,6 @@ from judge.models import ( LanguageLimit, LanguageTemplate, Problem, - ProblemClarification, ProblemTranslation, Profile, Solution, @@ -147,23 +146,6 @@ class LanguageTemplateInline(admin.TabularInline): 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): def __init__(self, *args, **kwargs): super(ProblemSolutionForm, self).__init__(*args, **kwargs) @@ -256,7 +238,6 @@ class ProblemAdmin(CompareVersionAdmin): inlines = [ LanguageLimitInline, LanguageTemplateInline, - ProblemClarificationInline, ProblemSolutionInline, ProblemTranslationInline, ] diff --git a/judge/migrations/0133_auto_20221013_0850.py b/judge/migrations/0133_auto_20221013_0850.py new file mode 100644 index 0000000..b210016 --- /dev/null +++ b/judge/migrations/0133_auto_20221013_0850.py @@ -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", + ), + ] diff --git a/judge/models/__init__.py b/judge/models/__init__.py index 256156f..166ec3b 100644 --- a/judge/models/__init__.py +++ b/judge/models/__init__.py @@ -15,6 +15,7 @@ from judge.models.contest import ( ContestSubmission, ContestTag, Rating, + ContestProblemClarification, ) from judge.models.interface import BlogPost, MiscConfig, NavigationBar, validate_regex from judge.models.message import PrivateMessage, PrivateMessageThread @@ -23,7 +24,6 @@ from judge.models.problem import ( LanguageTemplate, License, Problem, - ProblemClarification, ProblemGroup, ProblemTranslation, ProblemType, diff --git a/judge/models/contest.py b/judge/models/contest.py index 8b90f77..1cd5795 100644 --- a/judge/models/contest.py +++ b/judge/models/contest.py @@ -29,6 +29,7 @@ __all__ = [ "ContestProblem", "ContestSubmission", "Rating", + "ContestProblemClarification", ] @@ -766,6 +767,10 @@ class ContestProblem(models.Model): ], ) + @property + def clarifications(self): + return ContestProblemClarification.objects.filter(problem=self) + class Meta: unique_together = ("problem", "contest") verbose_name = _("contest problem") @@ -853,3 +858,13 @@ class ContestMoss(models.Model): unique_together = ("contest", "problem", "language") verbose_name = _("contest moss result") 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 + ) diff --git a/judge/models/problem.py b/judge/models/problem.py index 186128a..c49880a 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -28,7 +28,6 @@ __all__ = [ "ProblemType", "Problem", "ProblemTranslation", - "ProblemClarification", "License", "Solution", "TranslatedProblemQuerySet", @@ -490,10 +489,6 @@ class Problem(models.Model): def i18n_name(self, value): self._i18n_name = value - @property - def clarifications(self): - return ProblemClarification.objects.filter(problem=self) - def update_stats(self): self.user_count = ( self.submission_set.filter( @@ -613,16 +608,6 @@ class ProblemTranslation(models.Model): 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): problem = models.ForeignKey( Problem, diff --git a/judge/views/blog.py b/judge/views/blog.py index b3f508d..061fee0 100644 --- a/judge/views/blog.py +++ b/judge/views/blog.py @@ -14,7 +14,7 @@ from judge.models import ( Contest, Language, Problem, - ProblemClarification, + ContestProblemClarification, Profile, Submission, Ticket, @@ -50,8 +50,8 @@ class FeedView(ListView): if self.request.user.is_authenticated: participation = self.request.profile.current_contest if participation: - clarifications = ProblemClarification.objects.filter( - problem__in=participation.contest.problems.all() + clarifications = ContestProblemClarification.objects.filter( + problem__in=participation.contest.contest_problems.all() ) context["has_clarifications"] = clarifications.count() > 0 context["clarifications"] = clarifications.order_by("-date") diff --git a/judge/views/contests.py b/judge/views/contests.py index f179c5c..532ec15 100644 --- a/judge/views/contests.py +++ b/judge/views/contests.py @@ -65,7 +65,7 @@ from judge.models import ( Problem, Profile, Submission, - ProblemClarification, + ContestProblemClarification, ) from judge.tasks import run_moss from judge.utils.celery import redirect_to_task_status @@ -1179,7 +1179,7 @@ class ContestTagDetail(TitleMixin, ContestTagDetailAjax): return _("Contest tag: %s") % self.object.name -class ProblemClarificationForm(forms.Form): +class ContestProblemClarificationForm(forms.Form): body = forms.CharField( widget=HeavyPreviewPageDownWidget( preview=reverse_lazy("comment_preview"), @@ -1190,12 +1190,12 @@ class ProblemClarificationForm(forms.Form): def __init__(self, request, *args, **kwargs): self.request = request - super(ProblemClarificationForm, self).__init__(*args, **kwargs) + super(ContestProblemClarificationForm, self).__init__(*args, **kwargs) self.fields["body"].widget.attrs.update({"placeholder": _("Issue description")}) class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView): - form_class = ProblemClarificationForm + form_class = ContestProblemClarificationForm template_name = "contest/clarification.html" def get_form_kwargs(self): @@ -1225,12 +1225,13 @@ class NewContestClarificationView(ContestMixin, TitleMixin, SingleObjectFormView problem_code = self.request.POST["problem"] description = form.cleaned_data["body"] - clarification = ProblemClarification(description=description) - clarification.problem = Problem.objects.get(code=problem_code) + clarification = ContestProblemClarification(description=description) + clarification.problem = get_object_or_404( + ContestProblem, contest=self.get_object(), problem__code=problem_code + ) clarification.save() - link = reverse("home") - return HttpResponseRedirect(link) + return HttpResponseRedirect(reverse("problem_list")) def get_title(self): return "New clarification for %s" % self.object.name @@ -1264,26 +1265,27 @@ class ContestClarificationAjax(ContestMixin, DetailView): minutes=polling_time ) - queryset = list( - ProblemClarification.objects.filter( - problem__in=self.object.problems.all(), date__gte=last_one_minute - ).values("problem", "problem__name", "description") + queryset = ContestProblemClarification.objects.filter( + problem__in=self.object.contest_problems.all(), date__gte=last_one_minute ) problems = list( ContestProblem.objects.filter(contest=self.object) .order_by("order") - .values("problem") + .values_list("problem__code", flat=True) ) - problems = [i["problem"] for i in problems] - for cla in queryset: - cla["order"] = self.object.get_label_for_problem( - problems.index(cla["problem"]) - ) + res = [] + for clarification in queryset: + value = { + "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( - queryset, safe=False, json_dumps_params={"ensure_ascii": False} - ) + return JsonResponse(res, safe=False, json_dumps_params={"ensure_ascii": False}) def update_contest_mode(request): diff --git a/judge/views/organization.py b/judge/views/organization.py index b3c23b8..5f8a0b2 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -930,7 +930,12 @@ class EditOrganizationContest( for problem_form in problem_formset.deleted_objects: problem_form.delete() 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 return self.render_to_response( @@ -1062,9 +1067,9 @@ class EditOrganizationBlog( _("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"] - BlogPost.objects.get(pk = self.blog_id).delete() + BlogPost.objects.get(pk=self.blog_id).delete() def get(self, request, *args, **kwargs): res = self.setup_blog(request, *args, **kwargs) @@ -1076,10 +1081,13 @@ class EditOrganizationBlog( res = self.setup_blog(request, *args, **kwargs) if res: return res - if request.POST['action'] == 'Delete': + if request.POST["action"] == "Delete": self.create_notification("Delete blog") - self.delete_blog(request , *args , **kwargs) - cur_url = reverse("organization_pending_blogs", args=(self.organization_id,self.organization.slug) ) + self.delete_blog(request, *args, **kwargs) + cur_url = reverse( + "organization_pending_blogs", + args=(self.organization_id, self.organization.slug), + ) return HttpResponseRedirect(cur_url) else: return super().post(request, *args, **kwargs) @@ -1090,15 +1098,13 @@ class EditOrganizationBlog( def get_title(self): 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) link = reverse( "edit_organization_blog", args=[self.organization.id, self.organization.slug, self.blog_id], ) - html = ( - f'{blog.title} - {self.organization.name}' - ) + html = f'{blog.title} - {self.organization.name}' post_authors = blog.authors.all() posible_user = self.organization.admins.all() | post_authors for user in posible_user: @@ -1107,11 +1113,11 @@ class EditOrganizationBlog( notification = Notification( owner=user, author=self.request.profile, - category= action, + category=action, html_link=html, ) notification.save() - + def form_valid(self, form): with transaction.atomic(), revisions.create_revision(): res = super(EditOrganizationBlog, self).form_valid(form) @@ -1120,6 +1126,7 @@ class EditOrganizationBlog( self.create_notification("Edit blog") return res + class PendingBlogs( LoginRequiredMixin, TitleMixin, diff --git a/judge/views/problem.py b/judge/views/problem.py index ba9790c..2f87c43 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -40,7 +40,7 @@ from judge.models import ( Judge, Language, Problem, - ProblemClarification, + ContestProblemClarification, ProblemGroup, ProblemTranslation, ProblemType, @@ -251,7 +251,7 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView): context["contest_problem"] = contest_problem if contest_problem: - clarifications = self.object.clarifications + clarifications = contest_problem.clarifications context["has_clarifications"] = clarifications.count() > 0 context["clarifications"] = clarifications.order_by("-date") context["submission_limit"] = contest_problem.max_submissions @@ -665,8 +665,8 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView if self.request.user.is_authenticated: participation = self.request.profile.current_contest if participation: - clarifications = ProblemClarification.objects.filter( - problem__in=participation.contest.problems.all() + clarifications = ContestProblemClarification.objects.filter( + problem__in=participation.contest.contest_problems.all() ) context["has_clarifications"] = clarifications.count() > 0 context["clarifications"] = clarifications.order_by("-date") diff --git a/templates/blog/list.html b/templates/blog/list.html index e330845..976e064 100644 --- a/templates/blog/list.html +++ b/templates/blog/list.html @@ -106,9 +106,9 @@