Skip to content

Show problem limits #499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions oioioi/contestexcl/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ def _modify_contestexcl(
('problemstatementconfig', 0, 0, 0, 1),
('rankingvisibilityconfig', 0, 0, 0, 1),
('registrationavailabilityconfig', 0, 0, 0, 1),
('limitsvisibilityconfig', 0, 0, 0, 1),
('balloonsdeliveryaccessdata', 0, 0, 0, 1),
('statistics_config', 0, 0, 0, 1),
('exclusivenessconfig_set', len(excl_start_date_forms), 0, 0, 1000),
Expand Down
118 changes: 118 additions & 0 deletions oioioi/contests/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
from django.db.models import Min, Max, Q

from oioioi.base.utils import (
ObjectWithMixins,
Expand All @@ -21,8 +22,10 @@
from oioioi.base.utils.query_helpers import Q_always_true
from oioioi.contests.models import (
Contest,
ProblemInstance,
ProblemStatementConfig,
RankingVisibilityConfig,
LimitsVisibilityConfig,
Round,
RoundTimeExtension,
ScoreReport,
Expand All @@ -41,8 +44,11 @@
last_break_between_rounds,
rounds_times,
visible_problem_instances,
process_instances_to_limits,
)
from oioioi.newsfeed import default_app_config
from oioioi.problems.controllers import ProblemController
from oioioi.programs.models import Test

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -586,6 +592,19 @@ def can_see_statement(self, request_or_context, problem_instance):
def default_can_see_statement(self, request_or_context, problem_instance):
return True

def can_see_problems_limits(self, request):
context = self.make_context(request)
lvc = LimitsVisibilityConfig.objects.filter(contest=context.contest)
if lvc.exists() and lvc[0].visible == 'YES':
return True
elif lvc.exists() and lvc[0].visible == 'NO':
return False
else:
return self.default_can_see_problems_limits(request)

def default_can_see_problems_limits(self, request):
return False

def can_submit(self, request, problem_instance, check_round_times=True):
"""Determines if the current user is allowed to submit a solution for
the given problem.
Expand Down Expand Up @@ -641,6 +660,105 @@ def is_submissions_limit_exceeded(self, request, problem_instance, kind=None):
request, problem_instance, kind
)

def get_problems_limits(self, request):
"""Returns a dictionary containing data about time and memory limits for a given ProblemInstance:
ProblemInstanceID -> {
'default': (min_time, max_time, min_memory, max_memory),
'cpp': (min_time, max_time, min_memory, max_memory),
'py': (min_time, max_time, min_memory, max_memory)
}
Corresponding dictionary is None if no limits exist
"""

instances = ProblemInstance.objects.filter(contest=request.contest).annotate(
# default limits
min_time=Min('test__time_limit', filter=Q(test__is_active=True)),
max_time=Max('test__time_limit', filter=Q(test__is_active=True)),
min_memory=Min('test__memory_limit', filter=Q(test__is_active=True)),
max_memory=Max('test__memory_limit', filter=Q(test__is_active=True)),

# cpp overridden limits
cpp_min_time=Min(
'test__languageoverridefortest__time_limit',
filter=Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),
cpp_max_time=Max(
'test__languageoverridefortest__time_limit',
filter=Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),
cpp_min_memory=Min(
'test__languageoverridefortest__memory_limit',
filter=Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),
cpp_max_memory=Max(
'test__languageoverridefortest__memory_limit',
filter=Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),

# python overridden limits
py_min_time=Min(
'test__languageoverridefortest__time_limit',
filter=Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
py_max_time=Max(
'test__languageoverridefortest__time_limit',
filter=Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
py_min_memory=Min(
'test__languageoverridefortest__memory_limit',
filter=Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
py_max_memory=Max(
'test__languageoverridefortest__memory_limit',
filter=Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),

# non-overridden test limits in cpp
cpp_min_time_non_overridden=Min(
'test__time_limit',
filter=~Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),
cpp_max_time_non_overridden=Max(
'test__time_limit',
filter=~Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),
cpp_min_memory_non_overridden=Min(
'test__memory_limit',
filter=~Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),
cpp_max_memory_non_overridden=Max(
'test__memory_limit',
filter=~Q(test__languageoverridefortest__language='cpp') & Q(test__is_active=True)
),

# non-overridden test limits in python
py_min_time_non_overridden=Min(
'test__time_limit',
filter=~Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
py_max_time_non_overridden=Max(
'test__time_limit',
filter=~Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
py_min_memory_non_overridden=Min(
'test__memory_limit',
filter=~Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
py_max_memory_non_overridden=Max(
'test__memory_limit',
filter=~Q(test__languageoverridefortest__language='py') & Q(test__is_active=True)
),
).values(
'id',
'min_time', 'max_time', 'min_memory', 'max_memory',
'cpp_min_time', 'cpp_max_time', 'cpp_min_memory', 'cpp_max_memory',
'cpp_min_time_non_overridden', 'cpp_max_time_non_overridden', 'cpp_min_memory_non_overridden', 'cpp_max_memory_non_overridden',
'py_min_time', 'py_max_time', 'py_min_memory', 'py_max_memory',
'py_min_time_non_overridden', 'py_max_time_non_overridden', 'py_min_memory_non_overridden', 'py_max_memory_non_overridden'
)

return process_instances_to_limits(instances)

def adjust_submission_form(self, request, form, problem_instance):
# by default delegate to ProblemController
problem_instance.problem.controller.adjust_submission_form(
Expand Down
27 changes: 27 additions & 0 deletions oioioi/contests/migrations/0020_limitsvisibilityconfig.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please change migration number to 0021 and update depdendencies

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.20 on 2025-03-23 13:34

from django.db import migrations, models
import django.db.models.deletion
import oioioi.base.fields


class Migration(migrations.Migration):

dependencies = [
('contests', '0019_submissionmessage'),
]

operations = [
migrations.CreateModel(
name='LimitsVisibilityConfig',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('visible', oioioi.base.fields.EnumField(default='NO', help_text="Determines whether participants can see problems' time and memory limits", max_length=64, verbose_name='limits visibility')),
('contest', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='contests.contest')),
],
options={
'verbose_name': 'limits visibility config',
'verbose_name_plural': 'limits visibility configs',
},
),
]
21 changes: 21 additions & 0 deletions oioioi/contests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,27 @@ class Meta(object):
verbose_name_plural = _("ranking visibility configs")


limits_visibility_options = EnumRegistry()
limits_visibility_options.register('YES', _("Visible"))
limits_visibility_options.register('NO', _("Not visible"))


class LimitsVisibilityConfig(models.Model):
contest = models.OneToOneField('contests.Contest', on_delete=models.CASCADE)
visible = EnumField(
limits_visibility_options,
default='NO',
verbose_name=_("limits visibility"),
help_text=_(
"Determines whether participants can see problems' time and memory limits"
),
)

class Meta(object):
verbose_name = _("limits visibility config")
verbose_name_plural = _("limits visibility configs")


registration_availability_options = EnumRegistry()
registration_availability_options.register('YES', _("Open"))
registration_availability_options.register('NO', _("Closed"))
Expand Down
15 changes: 15 additions & 0 deletions oioioi/contests/static/common/contests.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,18 @@ table.table-striped > tbody > tr.problemlist-subheader {
margin: 3rem;
margin-bottom: 4rem;
}

.problem-name-column {
width: 100%;
}

.problem-limits-row {
background-color: transparent !important;
border-color: transparent !important;
white-space: nowrap;
}

.problem-limits-row td {
border: none;
white-space: nowrap;
}
27 changes: 24 additions & 3 deletions oioioi/contests/templates/contests/problems_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ <h1>{% trans "Problems" %}</h1>
<thead>
<tr>
<th></th>
<th>{% trans "Name" %}</th>
<th class="problem-name-column">{% trans "Name" %}</th>
{% if show_problems_limits %}
<th>
{% trans "Limits" %}
</th>
{% endif %}
{% if show_submissions_limit %}
<th class="text-right">
{% if user.is_authenticated %}
Expand All @@ -31,8 +36,7 @@ <h1>{% trans "Problems" %}</h1>
</tr>
</thead>
<tbody>
{% for pi, statement_visible, round_time, result, submissions_left, submissions_limit, can_submit in problem_instances %}

{% for pi, statement_visible, round_time, problem_limits, result, submissions_left, submissions_limit, can_submit in problem_instances %}
{% if show_rounds %}
{% ifchanged pi.round %}
<tr class="problemlist-subheader">
Expand All @@ -53,6 +57,23 @@ <h1>{% trans "Problems" %}</h1>
{{ pi.problem.name }}
{% endif %}
</td>
{% if show_problems_limits %}
<td class="text-right">
{% if problem_limits %}
<table>
{% for row in problem_limits %}
<tr class="problem-limits-row">
{% for entry in row %}
<td>
{{ entry }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
</td>
{% endif %}
{% if show_submissions_limit %}
<td class="text-right">
{% if submissions_left == None %}
Expand Down
1 change: 1 addition & 0 deletions oioioi/contests/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def make_empty_contest_formset():
('problemstatementconfig', 0, 0, 0, 1),
('rankingvisibilityconfig', 0, 0, 0, 1),
('registrationavailabilityconfig', 0, 0, 0, 1),
('limitsvisibilityconfig', 0, 0, 0, 1),
('balloonsdeliveryaccessdata', 1, 0, 0, 1),
('statistics_config', 1, 0, 0, 1),
('exclusivenessconfig_set', 0, 0, 0, 1000),
Expand Down
Loading