Skip to content

Commit

Permalink
Add automated problem type voting
Browse files Browse the repository at this point in the history
If the number of distinct voters exceeds VNOJ_PROBLEM_TYPE_VOTING_VOTERS_THRESHOLD:
  - Sort the votes of each type in descending order
  - Filter the types whose number of votes exceeds VNOJ_PROBLEM_TYPE_VOTING_VOTES_LOWERBOUND
  - Get the first VNOJ_PROBLEM_TYPE_VOTING_TYPES_UPPERBOUND records
  • Loading branch information
magnified103 committed Apr 9, 2024
1 parent 7a4cc9d commit 9438621
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 2 deletions.
4 changes: 4 additions & 0 deletions dmoj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
VNOJ_CONTEST_CHEATING_BAN_MESSAGE = 'Banned for multiple cheating offenses during contests'
VNOJ_MAX_DISQUALIFICATIONS_BEFORE_BANNING = 3

VNOJ_PROBLEM_TYPE_VOTING_VOTERS_THRESHOLD = 10
VNOJ_PROBLEM_TYPE_VOTING_VOTES_LOWERBOUND = 4
VNOJ_PROBLEM_TYPE_VOTING_TYPES_UPPERBOUND = 3

# List of subdomain that will be ignored in organization subdomain middleware
VNOJ_IGNORED_ORGANIZATION_SUBDOMAINS = ['oj', 'www', 'localhost']

Expand Down
2 changes: 1 addition & 1 deletion judge/admin/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class ProblemAdmin(NoBatchDeleteMixin, VersionAdmin):
),
}),
(_('Social Media'), {'classes': ('collapse',), 'fields': ('og_image', 'summary')}),
(_('Taxonomy'), {'fields': ('types', 'allow_type_voting', 'group')}),
(_('Taxonomy'), {'fields': ('types', 'allow_type_voting', 'automated_type_voting', 'group')}),
(_('Points'), {'fields': (('points', 'partial'), 'short_circuit')}),
(_('Limits'), {'fields': ('time_limit', 'memory_limit')}),
(_('Language'), {'fields': ('allowed_languages',)}),
Expand Down
3 changes: 2 additions & 1 deletion judge/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def __init__(self, *args, **kwargs):

if not manage_type_voting:
self.fields.pop('allow_type_voting')
self.fields.pop('automated_type_voting')

self.fields['testers'].help_text = \
str(self.fields['testers'].help_text) + ' ' + \
Expand Down Expand Up @@ -230,7 +231,7 @@ class Meta:
model = Problem
fields = ['is_public', 'code', 'name', 'time_limit', 'memory_limit', 'points', 'partial',
'statement_file', 'source', 'types', 'group', 'submission_source_visibility_mode',
'testcase_visibility_mode', 'description', 'testers', 'allow_type_voting']
'testcase_visibility_mode', 'description', 'testers', 'allow_type_voting', 'automated_type_voting']
widgets = {
'types': Select2MultipleWidget,
'group': Select2Widget,
Expand Down
19 changes: 19 additions & 0 deletions judge/migrations/0205_automated_problem_type_voting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.23 on 2024-03-24 05:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('judge', '0204_problem_type_voting'),
]

operations = [
migrations.AddField(
model_name='problem',
name='automated_type_voting',
field=models.BooleanField(default=True, help_text='Automatically set problem types based on '
'a certain threshold of votes.'),
),
]
3 changes: 3 additions & 0 deletions judge/models/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ class Problem(models.Model):

allow_type_voting = models.BooleanField(default=False, help_text=_('Allow public voting on problem types.'))

automated_type_voting = models.BooleanField(default=True, help_text=_('Automatically set problem types based on '
'a certain threshold of votes.'))

allow_view_feedback = models.BooleanField(
help_text=_('Allow user to view checker feedback.'),
default=False,
Expand Down
20 changes: 20 additions & 0 deletions judge/views/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,26 @@ def form_valid(self, form):
problem_type=problem_type,
) for problem_type in new_types)

if self.object.automated_type_voting:
voters = ProblemTypeVote.objects.filter(problem=self.object).values('user').distinct().count()
if voters >= settings.VNOJ_PROBLEM_TYPE_VOTING_VOTERS_THRESHOLD:
# If the number of distinct voters exceeds VNOJ_PROBLEM_TYPE_VOTING_VOTERS_THRESHOLD:
# - Sort the votes of each type in descending order
# - Filter the types whose number of votes exceeds VNOJ_PROBLEM_TYPE_VOTING_VOTES_LOWERBOUND
# - Get the first VNOJ_PROBLEM_TYPE_VOTING_TYPES_UPPERBOUND records
problem_types = (
ProblemType.objects.annotate(votes=Count('problemtypevote',
filter=Q(problemtypevote__problem=self.object)))
.order_by('-votes')
.filter(votes__gte=settings.VNOJ_PROBLEM_TYPE_VOTING_VOTES_LOWERBOUND)
[:settings.VNOJ_PROBLEM_TYPE_VOTING_TYPES_UPPERBOUND])
self.object.types.add(*problem_types)

# Disable public voting
self.object.allow_type_voting = False

self.object.save()

return super().form_valid(form)

def form_invalid(self, form):
Expand Down

0 comments on commit 9438621

Please sign in to comment.