Add problem volunteer

This commit is contained in:
cuom1999 2022-05-02 21:44:14 -05:00
parent e51129d36f
commit e70618ed19
15 changed files with 396 additions and 5 deletions

View file

@ -11,9 +11,11 @@ from judge.admin.runtime import JudgeAdmin, LanguageAdmin
from judge.admin.submission import SubmissionAdmin
from judge.admin.taxon import ProblemGroupAdmin, ProblemTypeAdmin
from judge.admin.ticket import TicketAdmin
from judge.admin.volunteer import VolunteerProblemVoteAdmin
from judge.models import BlogPost, Comment, CommentLock, Contest, ContestParticipation, \
ContestTag, Judge, Language, License, MiscConfig, NavigationBar, Organization, \
OrganizationRequest, Problem, ProblemGroup, ProblemPointsVote, ProblemType, Profile, Submission, Ticket
OrganizationRequest, Problem, ProblemGroup, ProblemPointsVote, ProblemType, Profile, Submission, Ticket, \
VolunteerProblemVote
admin.site.register(BlogPost, BlogPostAdmin)
@ -37,3 +39,4 @@ admin.site.register(ProblemType, ProblemTypeAdmin)
admin.site.register(Profile, ProfileAdmin)
admin.site.register(Submission, SubmissionAdmin)
admin.site.register(Ticket, TicketAdmin)
admin.site.register(VolunteerProblemVote, VolunteerProblemVoteAdmin)

18
judge/admin/volunteer.py Normal file
View file

@ -0,0 +1,18 @@
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.translation import gettext, gettext_lazy as _, ungettext
from judge.models import VolunteerProblemVote
class VolunteerProblemVoteAdmin(admin.ModelAdmin):
fields = ('voter', 'problem', 'time', 'thinking_points', 'knowledge_points', 'feedback')
readonly_fields = ('time', 'problem', 'voter')
list_display = ('voter', 'problem_link', 'time', 'thinking_points', 'knowledge_points', 'feedback')
date_hierarchy = 'time'
def problem_link(self, obj):
url = reverse('admin:judge_problem_change', args=(obj.problem.id,))
return format_html(f"<a href='{url}'>{obj.problem.code}</a>")
problem_link.short_description = _('Problem')
problem_link.admin_order_field = 'problem__code'

View file

@ -0,0 +1,36 @@
# Generated by Django 2.2.25 on 2022-05-02 16:56
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('judge', '0122_auto_20220425_1202'),
]
operations = [
migrations.AlterModelOptions(
name='problem',
options={'permissions': (('see_private_problem', 'See hidden problems'), ('edit_own_problem', 'Edit own problems'), ('edit_all_problem', 'Edit all problems'), ('edit_public_problem', 'Edit all public problems'), ('clone_problem', 'Clone problem'), ('change_public_visibility', 'Change is_public field'), ('change_manually_managed', 'Change is_manually_managed field'), ('see_organization_problem', 'See organization-private problems'), ('suggest_problem_changes', 'Suggest changes to problem')), 'verbose_name': 'problem', 'verbose_name_plural': 'problems'},
),
migrations.CreateModel(
name='VolunteerProblemVote',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.DateTimeField(auto_now_add=True)),
('knowledge_points', models.PositiveIntegerField(help_text='Points awarded by knowledge difficulty', verbose_name='knowledge points')),
('thinking_points', models.PositiveIntegerField(help_text='Points awarded by thinking difficulty', verbose_name='thinking points')),
('feedback', models.TextField(blank=True, verbose_name='feedback')),
('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='volunteer_user_votes', to='judge.Problem')),
('types', models.ManyToManyField(help_text="The type of problem, as shown on the problem's page.", to='judge.ProblemType', verbose_name='problem types')),
('voter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='volunteer_problem_votes', to='judge.Profile')),
],
options={
'verbose_name': 'volunteer vote',
'verbose_name_plural': 'volunteer votes',
'unique_together': {('voter', 'problem')},
},
),
]

View file

@ -14,6 +14,7 @@ from judge.models.profile import Organization, OrganizationRequest, Profile, Fri
from judge.models.runtime import Judge, Language, RuntimeVersion
from judge.models.submission import SUBMISSION_RESULT, Submission, SubmissionSource, SubmissionTestCase
from judge.models.ticket import Ticket, TicketMessage
from judge.models.volunteer import VolunteerProblemVote
revisions.register(Profile, exclude=['points', 'last_access', 'ip', 'rating'])
revisions.register(Problem, follow=['language_limits'])

View file

@ -377,6 +377,7 @@ class Problem(models.Model):
save.alters_data = True
def can_vote(self, request):
return False
user = request.user
if not user.is_authenticated:
return False

28
judge/models/volunteer.py Normal file
View file

@ -0,0 +1,28 @@
from django.db import models
from django.db.models import CASCADE
from django.utils.translation import gettext_lazy as _
from judge.models import Profile, Problem, ProblemType
__all__ = ['VolunteerProblemVote']
class VolunteerProblemVote(models.Model):
voter = models.ForeignKey(Profile, related_name='volunteer_problem_votes', on_delete=CASCADE)
problem = models.ForeignKey(Problem, related_name='volunteer_user_votes', on_delete=CASCADE)
time = models.DateTimeField(auto_now_add=True)
knowledge_points = models.PositiveIntegerField(verbose_name=_('knowledge points'),
help_text=_('Points awarded by knowledge difficulty'))
thinking_points = models.PositiveIntegerField(verbose_name=_('thinking points'),
help_text=_('Points awarded by thinking difficulty'))
types = models.ManyToManyField(ProblemType, verbose_name=_('problem types'),
help_text=_('The type of problem, '
"as shown on the problem's page."))
feedback = models.TextField(verbose_name=_('feedback'), blank=True)
class Meta:
verbose_name = _('volunteer vote')
verbose_name_plural = _('volunteer votes')
unique_together = ['voter', 'problem']
def __str__(self):
return f'{self.voter} for {self.problem.code}'

35
judge/views/internal.py Normal file
View file

@ -0,0 +1,35 @@
from django.views.generic import ListView
from django.utils.translation import gettext as _, gettext_lazy
from django.db.models import Count
from django.http import HttpResponseForbidden
from judge.utils.diggpaginator import DiggPaginator
from judge.models import VolunteerProblemVote, Problem
class InternalProblem(ListView):
model = Problem
title = _('Internal problems')
template_name = 'internal/base.html'
paginate_by = 100
context_object_name = 'problems'
def get_paginator(self, queryset, per_page, orphans=0,
allow_empty_first_page=True, **kwargs):
return DiggPaginator(queryset, per_page, body=6, padding=2, orphans=orphans,
allow_empty_first_page=allow_empty_first_page, **kwargs)
def get_queryset(self):
queryset = Problem.objects.annotate(vote_count=Count('volunteer_user_votes')) \
.filter(vote_count__gte=1).order_by('-vote_count')
return queryset
def get_context_data(self, **kwargs):
context = super(InternalProblem, self).get_context_data(**kwargs)
context['page_type'] = 'problem'
context['title'] = self.title
return context
def get(self, request, *args, **kwargs):
if request.user.is_superuser:
return super(InternalProblem, self).get(request, *args, **kwargs)
return HttpResponseForbidden()

View file

@ -30,7 +30,7 @@ from judge.comments import CommentedDetailView
from judge.forms import ProblemCloneForm, ProblemSubmitForm, ProblemPointsVoteForm
from judge.models import ContestProblem, ContestSubmission, Judge, Language, Problem, ProblemClarification, \
ProblemGroup, ProblemTranslation, ProblemType, ProblemPointsVote, RuntimeVersion, Solution, Submission, SubmissionSource, \
TranslatedProblemForeignKeyQuerySet, Organization
TranslatedProblemForeignKeyQuerySet, Organization , VolunteerProblemVote
from judge.pdf_problems import DefaultPdfMaker, HAS_PDF
from judge.utils.diggpaginator import DiggPaginator
from judge.utils.opengraph import generate_opengraph
@ -594,6 +594,7 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
cf_logger = logging.getLogger('judge.ml.collab_filter')
class ProblemFeed(ProblemList):
model = Problem
context_object_name = 'problems'
@ -640,6 +641,9 @@ class ProblemFeed(ProblemList):
if self.feed_type == 'new':
return queryset.order_by('-date')
elif user and self.feed_type == 'volunteer':
voted_problems = user.volunteer_problem_votes.values_list('problem', flat=True)
return queryset.exclude(id__in=voted_problems).order_by('?')
if not settings.ML_OUTPUT_PATH or not user:
return queryset.order_by('?')

33
judge/views/volunteer.py Normal file
View file

@ -0,0 +1,33 @@
from django.http import HttpResponseBadRequest, JsonResponse
from django.db import transaction
from judge.models import VolunteerProblemVote, Problem, ProblemType
def vote_problem(request):
if not request.user or not request.user.has_perm('judge.suggest_problem_changes'):
return HttpResponseBadRequest()
if not request.method == 'POST':
return HttpResponseBadRequest()
try:
types_id = request.POST.getlist('types[]')
types = ProblemType.objects.filter(id__in=types_id)
problem = Problem.objects.get(code=request.POST['problem'])
knowledge_points = request.POST['knowledge_points']
thinking_points = request.POST['thinking_points']
feedback = request.POST['feedback']
except Exception as e:
return HttpResponseBadRequest()
with transaction.atomic():
vote, _ = VolunteerProblemVote.objects.get_or_create(
voter=request.profile,
problem=problem,
defaults={'knowledge_points': 0, 'thinking_points': 0},
)
vote.knowledge_points = knowledge_points
vote.thinking_points = thinking_points
vote.feedback = feedback
vote.types.set(types)
vote.save()
return JsonResponse({})