From b82ae560b15117d64c8e1dce4a84c3c05af859fe Mon Sep 17 00:00:00 2001 From: Mateusz Masiarz Date: Mon, 23 Sep 2024 11:18:16 +0200 Subject: [PATCH] Set oicompare language to user's language (#403) * Set oicompare language to user's language * Remove debug * Fix bug * Allow changing oicompare format * Change test for oicompare --- oioioi/contestexcl/tests.py | 1 + oioioi/problems/controllers.py | 12 +++++ oioioi/programs/admin.py | 37 ++++++++++++++ oioioi/programs/controllers.py | 3 +- oioioi/programs/handlers.py | 1 + ...msubmission_user_language_code_and_more.py | 49 +++++++++++++++++++ oioioi/programs/models.py | 43 ++++++++++++++++ oioioi/programs/tests.py | 30 +++++++++++- oioioi/programs/utils.py | 15 ++++++ requirements.txt | 2 +- 10 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 oioioi/programs/migrations/0020_programsubmission_user_language_code_and_more.py diff --git a/oioioi/contestexcl/tests.py b/oioioi/contestexcl/tests.py index 1ac43b6b0..88367d72c 100644 --- a/oioioi/contestexcl/tests.py +++ b/oioioi/contestexcl/tests.py @@ -189,6 +189,7 @@ def _modify_contestexcl( ('contestlogo', 0, 0, 0, 1), ('programs_config', 0, 0, 0, 1), ('contestcompiler_set', 0, 0, 0, 1000), + ('checkerformatforcontest', 0, 0, 0, 1), ) data = dict() for (name, total, initial, min_num, max_num) in formsets: diff --git a/oioioi/problems/controllers.py b/oioioi/problems/controllers.py index 382d5d895..7eeb60345 100644 --- a/oioioi/problems/controllers.py +++ b/oioioi/problems/controllers.py @@ -24,6 +24,7 @@ from oioioi.evalmgr.tasks import create_environ, delay_environ from oioioi.problems.models import ProblemStatistics, UserStatistics from oioioi.problems.utils import can_admin_problem +from oioioi.programs.utils import get_checker_format logger = logging.getLogger(__name__) @@ -148,6 +149,17 @@ def judge(self, submission, extra_args=None, is_rejudge=False): environ = create_environ() environ['extra_args'] = extra_args or {} environ['is_rejudge'] = is_rejudge + if hasattr(submission, 'programsubmission'): + user_lang = None + for code, lang in settings.LANGUAGES: + if code == submission.programsubmission.user_language_code: + user_lang = lang.lower() + break + if user_lang: + environ['user_language'] = user_lang + else: + environ['user_language'] = 'english' + environ['checker_format'] = environ['user_language'] + '_' + get_checker_format(submission.problem_instance) picontroller = submission.problem_instance.controller picontroller.fill_evaluation_environ(environ, submission) diff --git a/oioioi/programs/admin.py b/oioioi/programs/admin.py index 5ebd8b5a9..4132c982a 100644 --- a/oioioi/programs/admin.py +++ b/oioioi/programs/admin.py @@ -31,6 +31,8 @@ ProgramsConfig, ReportActionsConfig, Test, + CheckerFormatForContest, + CheckerFormatForProblem, ) @@ -447,3 +449,38 @@ def queryset(self, request, queryset): return queryset.filter(condition) else: return queryset + + +class CheckerFormatForContestInline(admin.StackedInline): + model = CheckerFormatForContest + category = _("Advanced") + + +class CheckerFormatForProblemInline(admin.StackedInline): + model = CheckerFormatForProblem + category = _("Advanced") + + +class CheckerFormatOverrideContestAdminMixin(object): + """Adds :class:`~oioioi.programs.models.CheckerFormatForContest` to an admin + panel. + """ + def __init__(self, *args, **kwargs): + super(CheckerFormatOverrideContestAdminMixin, self).__init__(*args, **kwargs) + self.inlines = tuple(self.inlines) + (CheckerFormatForContestInline,) + + +ContestAdmin.mix_in(CheckerFormatOverrideContestAdminMixin) + + +class CheckerFormatOverrideProblemAdminMixin(object): + """Adds :class:`~oioioi.programs.models.CheckerFormatForProblem` to an admin + panel. + """ + + def __init__(self, *args, **kwargs): + super(CheckerFormatOverrideProblemAdminMixin, self).__init__(*args, **kwargs) + self.inlines = tuple(self.inlines) + (CheckerFormatForProblemInline,) + + +ProblemInstanceAdmin.mix_in(CheckerFormatOverrideProblemAdminMixin) diff --git a/oioioi/programs/controllers.py b/oioioi/programs/controllers.py index 50eab91d7..924534ff0 100644 --- a/oioioi/programs/controllers.py +++ b/oioioi/programs/controllers.py @@ -11,7 +11,7 @@ from django.template.loader import render_to_string from django.urls import reverse from django.utils.safestring import mark_safe -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, get_language_from_request from oioioi.base.preferences import ensure_preferences_exist_for_user from oioioi.base.utils.inputs import narrow_input_field @@ -529,6 +529,7 @@ def create_submission( ), ), date=request.timestamp, + user_language_code=get_language_from_request(request), ) file = form_data['file'] diff --git a/oioioi/programs/handlers.py b/oioioi/programs/handlers.py index acddbfac8..03bcee9d2 100755 --- a/oioioi/programs/handlers.py +++ b/oioioi/programs/handlers.py @@ -289,6 +289,7 @@ def run_tests(env, kind=None, **kwargs): job['check_output'] = env.get('check_outputs', True) if env.get('checker'): job['chk_file'] = env['checker'] + job['checker_format'] = env.get('checker_format', 'english_abbreviated') if env.get('save_outputs'): job.setdefault('out_file', _make_filename(env, test_name + '.out')) job['upload_out'] = True diff --git a/oioioi/programs/migrations/0020_programsubmission_user_language_code_and_more.py b/oioioi/programs/migrations/0020_programsubmission_user_language_code_and_more.py new file mode 100644 index 000000000..7a0f84311 --- /dev/null +++ b/oioioi/programs/migrations/0020_programsubmission_user_language_code_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 4.2.16 on 2024-09-15 21:27 + +from django.db import migrations, models +import django.db.models.deletion +import oioioi.base.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('contests', '0018_contest_show_contest_rules'), + ('programs', '0019_add_limits_override'), + ] + + operations = [ + migrations.AddField( + model_name='programsubmission', + name='user_language_code', + field=models.CharField(blank=True, max_length=6, null=True, verbose_name='User language code'), + ), + migrations.CreateModel( + name='CheckerFormatForProblem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('format', oioioi.base.fields.EnumField(max_length=64, verbose_name='format')), + ('problem_instance', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='contests.probleminstance', verbose_name='problem instance')), + ], + options={ + 'verbose_name': 'checker format for problem', + 'verbose_name_plural': 'checker formats for problems', + 'ordering': ('problem_instance',), + 'unique_together': {('problem_instance', 'format')}, + }, + ), + migrations.CreateModel( + name='CheckerFormatForContest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('format', oioioi.base.fields.EnumField(help_text="Format of the checker output for this contest. Abbreviated describes the output difference, while Terse doesn't give any details.", max_length=64, verbose_name='format')), + ('contest', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='contests.contest', verbose_name='contest')), + ], + options={ + 'verbose_name': 'checker format for contest', + 'verbose_name_plural': 'checker formats for contests', + 'ordering': ('contest',), + 'unique_together': {('contest', 'format')}, + }, + ), + ] diff --git a/oioioi/programs/models.py b/oioioi/programs/models.py index b7bddb62f..8f8b0920a 100644 --- a/oioioi/programs/models.py +++ b/oioioi/programs/models.py @@ -261,6 +261,8 @@ class ProgramSubmission(Submission): source_length = models.IntegerField( verbose_name=_("Source code length"), blank=True, null=True ) + # Stores the language used by the user in the moment of submitting the solution + user_language_code = models.CharField(max_length=6, verbose_name=_("User language code"), blank=True, null=True) def save(self, *args, **kwargs): if self.source_file: @@ -461,3 +463,44 @@ def check_compilers_config(): check_compilers_config() + + +CheckerFormat = EnumRegistry() +CheckerFormat.register('terse', _("Terse")) +CheckerFormat.register('abbreviated', _("Abbreviated")) + + +class CheckerFormatForContest(models.Model): + """Overrides the default checker's format (abbreviated) for a contest.""" + + contest = models.OneToOneField( + Contest, verbose_name=_("contest"), on_delete=models.CASCADE + ) + format = EnumField( + CheckerFormat, + verbose_name=_("format"), + help_text=_("Format of the checker output for this contest. " + "Abbreviated describes the output difference, while " + "Terse doesn't give any details.") + ) + + class Meta(object): + verbose_name = _("checker format for contest") + verbose_name_plural = _("checker formats for contests") + ordering = ('contest',) + unique_together = ('contest', 'format') + + +class CheckerFormatForProblem(models.Model): + """Overrides the default checker's format (abbreviated) for a problem.""" + + problem_instance = models.OneToOneField( + ProblemInstance, verbose_name=_("problem instance"), on_delete=models.CASCADE + ) + format = EnumField(CheckerFormat, verbose_name=_("format")) + + class Meta(object): + verbose_name = _("checker format for problem") + verbose_name_plural = _("checker formats for problems") + ordering = ('problem_instance',) + unique_together = ('problem_instance', 'format') diff --git a/oioioi/programs/tests.py b/oioioi/programs/tests.py index 6061df187..82092088f 100644 --- a/oioioi/programs/tests.py +++ b/oioioi/programs/tests.py @@ -46,9 +46,11 @@ LanguageOverrideForTest, TestReport, check_compilers_config, + CheckerFormatForContest, + CheckerFormatForProblem, ) from oioioi.programs.problem_instance_utils import get_allowed_languages_dict -from oioioi.programs.utils import form_field_id_for_langs +from oioioi.programs.utils import form_field_id_for_langs, get_checker_format from oioioi.programs.views import _testreports_to_generate_outs from oioioi.sinolpack.models import ExtraConfig from oioioi.sinolpack.tests import get_test_filename @@ -544,7 +546,7 @@ def fake_send_notification( NotificationHandler.send_notification = fake_send_notification submission = Submission.objects.get(pk=1) - + environ = create_environ() environ['extra_args'] = {} environ['is_rejudge'] = False @@ -1986,3 +1988,27 @@ def test_proper_env_override(self): self.assertEqual( env_with_tests['tests']['1a']['exec_mem_limit'], tests[1].memory_limit ) + + +class TestOICompare(TestCase): + fixtures = ['test_contest', 'test_problem_instance', 'test_full_package'] + + def test_format(self): + contest = Contest.objects.get() + pi = ProblemInstance.objects.get() + for format_contest, format_problem, expected in [ + (None, None, 'abbreviated'), + ('terse', None, 'terse'), + ('terse', 'abbreviated', 'abbreviated'), + (None, 'terse', 'terse'), + ]: + CheckerFormatForContest.objects.all().delete() + CheckerFormatForProblem.objects.all().delete() + if format_problem is not None: + CheckerFormatForProblem.objects.create( + problem_instance=pi, format=format_problem + ) + if format_contest is not None: + CheckerFormatForContest.objects.create(contest=contest, format=format_contest) + + self.assertEqual(get_checker_format(pi), expected) diff --git a/oioioi/programs/utils.py b/oioioi/programs/utils.py index 48c8ca210..94a4c64a4 100644 --- a/oioioi/programs/utils.py +++ b/oioioi/programs/utils.py @@ -17,6 +17,8 @@ ModelProgramSubmission, ProgramSubmission, ReportActionsConfig, + CheckerFormatForContest, + CheckerFormatForProblem, ) @@ -215,3 +217,16 @@ def get_submittable_languages(): for _, lang_config in submittable_languages.items(): lang_config.setdefault('type', 'main') return submittable_languages + + +def get_checker_format(problem_instance): + try: + return CheckerFormatForProblem.objects.get(problem_instance=problem_instance).format + except CheckerFormatForProblem.DoesNotExist: + if problem_instance.contest: + try: + return CheckerFormatForContest.objects.get(contest=problem_instance.contest).format + except CheckerFormatForContest.DoesNotExist: + return getattr(settings, 'DEFAULT_CHECKER_FORMAT', 'abbreviated') + else: + return getattr(settings, 'DEFAULT_CHECKER_FORMAT', 'abbreviated') diff --git a/requirements.txt b/requirements.txt index 48667d776..80b42f931 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # These dependencies need to be installed from external sources, # therefore they must be listed here. Moreover, they cannot be listed in # setup.py, as pip is not able to install them. -http://github.com/sio2project/sioworkers/archive/refs/tags/v1.5.2.tar.gz +http://github.com/sio2project/sioworkers/archive/refs/tags/v1.5.3.tar.gz -e .