From b99138c114cf6b50b63e4ebd81132876a06f0c38 Mon Sep 17 00:00:00 2001 From: Iteron-dev Date: Sun, 28 Sep 2025 12:57:09 +0200 Subject: [PATCH 1/3] Fix: Add missing fixes from Ruff --- conftest.py | 5 +- ez_setup.py | 17 ++-- oioioi/__init__.py | 2 +- oioioi/acm/controllers.py | 8 +- oioioi/acm/score.py | 18 +--- oioioi/amppz/controllers.py | 4 +- oioioi/balloons/admin.py | 10 +-- oioioi/balloons/controllers.py | 2 +- oioioi/balloons/models.py | 10 +-- oioioi/balloons/views.py | 4 +- oioioi/base/admin.py | 24 +++--- oioioi/base/fields.py | 28 +++--- oioioi/base/forms.py | 16 ++-- oioioi/base/management/commands/migrate.py | 6 +- oioioi/base/management/commands/shell.py | 2 +- oioioi/base/menu.py | 19 +++-- oioioi/base/models.py | 14 +-- oioioi/base/permissions.py | 16 +++- oioioi/base/templatetags/all_with_prefix.py | 2 +- oioioi/base/templatetags/common_media.py | 6 +- oioioi/base/templatetags/menu.py | 4 +- oioioi/base/templatetags/simple_filters.py | 2 +- oioioi/base/tests/__init__.py | 11 ++- oioioi/base/tests/tests.py | 18 ++-- oioioi/base/utils/__init__.py | 27 +++--- oioioi/base/utils/archive.py | 4 +- oioioi/base/utils/color.py | 6 +- oioioi/base/utils/deps.py | 12 +-- oioioi/base/utils/execute.py | 4 +- oioioi/base/utils/input_with_generate.py | 4 +- oioioi/base/utils/loaders.py | 2 +- oioioi/base/utils/user_selection.py | 12 +-- oioioi/base/widgets.py | 4 +- oioioi/clock/views.py | 14 +-- oioioi/complaints/admin.py | 2 +- oioioi/complaints/views.py | 12 +-- oioioi/confirmations/controllers.py | 2 +- oioioi/contestexcl/admin.py | 4 +- oioioi/contestexcl/forms.py | 4 +- oioioi/contestexcl/middleware.py | 4 +- oioioi/contestexcl/models.py | 2 +- oioioi/contestexcl/tests.py | 2 +- oioioi/contestlogo/admin.py | 4 +- oioioi/contestlogo/models.py | 14 +-- oioioi/contestlogo/tests.py | 2 +- oioioi/contests/admin.py | 85 +++++++++---------- oioioi/contests/attachment_registration.py | 2 +- oioioi/contests/controllers.py | 2 +- oioioi/contests/date_registration.py | 30 ++++--- oioioi/contests/fields.py | 2 +- oioioi/contests/forms.py | 20 ++--- oioioi/contests/handlers.py | 2 +- .../commands/hidden_results_diff.py | 2 +- oioioi/contests/models.py | 28 +++--- oioioi/contests/scores.py | 14 +-- oioioi/contests/serializers.py | 2 +- oioioi/contests/templatetags/get_user_name.py | 2 +- oioioi/contests/tests/__init__.py | 4 +- oioioi/contests/tests/tests.py | 40 ++++----- oioioi/contests/utils.py | 29 +++---- oioioi/contests/views.py | 4 +- oioioi/dashboard/controllers.py | 2 +- oioioi/deployment/create_config.py | 2 +- oioioi/disqualification/admin.py | 14 ++- oioioi/disqualification/controllers.py | 20 ++--- oioioi/disqualification/models.py | 2 +- oioioi/disqualification/tests.py | 15 +++- oioioi/evalmgr/admin.py | 10 +-- oioioi/evalmgr/tasks.py | 12 +-- oioioi/evalmgr/tests/tests.py | 68 +++++++-------- oioioi/exportszu/forms.py | 2 +- oioioi/exportszu/utils.py | 10 +-- oioioi/exportszu/views.py | 2 +- oioioi/filetracker/client.py | 4 +- oioioi/filetracker/fields.py | 8 +- .../management/commands/collectgarbage.py | 2 +- oioioi/filetracker/utils.py | 6 +- oioioi/forum/admin.py | 50 +++++------ oioioi/forum/controllers.py | 2 +- oioioi/forum/forms.py | 10 +-- oioioi/forum/models.py | 15 ++-- oioioi/forum/tests.py | 5 +- oioioi/globalmessage/admin.py | 2 +- oioioi/ipauthsync/admin.py | 2 +- oioioi/ipauthsync/controllers.py | 2 +- .../management/commands/ipauthsyncd.py | 16 ++-- oioioi/ipauthsync/models.py | 2 +- .../management/commands/ipauth-dnsserver.py | 2 +- .../management/commands/ipdnsauth.py | 4 +- oioioi/livedata/tests.py | 2 +- oioioi/livedata/utils.py | 2 +- oioioi/livedata/views.py | 2 +- oioioi/mailsubmit/admin.py | 8 +- oioioi/mailsubmit/forms.py | 8 +- oioioi/mailsubmit/models.py | 6 +- oioioi/mailsubmit/utils.py | 2 +- oioioi/mailsubmit/views.py | 2 +- oioioi/mp/admin.py | 2 +- oioioi/mp/controllers.py | 4 +- oioioi/mp/forms.py | 2 +- oioioi/mp/score.py | 4 +- oioioi/mp/tests.py | 2 +- oioioi/newsfeed/models.py | 2 +- oioioi/notifications/processors.py | 8 +- oioioi/oi/admin.py | 6 +- oioioi/oi/controllers.py | 16 ++-- oioioi/oi/forms.py | 6 +- .../oi/management/commands/import_schools.py | 2 +- .../commands/oi_generate_dnsauth.py | 2 +- oioioi/oireports/forms.py | 8 +- oioioi/oireports/views.py | 24 +++--- oioioi/oisubmit/admin.py | 8 +- oioioi/oisubmit/controllers.py | 2 +- oioioi/oisubmit/forms.py | 4 +- oioioi/oisubmit/tests.py | 2 +- oioioi/oisubmit/views.py | 2 +- oioioi/pa/admin.py | 6 +- oioioi/pa/controllers.py | 16 ++-- oioioi/pa/score.py | 8 +- oioioi/pa/tests.py | 2 +- oioioi/participants/admin.py | 36 ++++---- oioioi/participants/controllers.py | 6 +- oioioi/participants/fields.py | 2 +- oioioi/participants/forms.py | 4 +- oioioi/participants/middleware.py | 6 +- oioioi/participants/models.py | 2 +- oioioi/participants/utils.py | 6 +- oioioi/plagiarism/forms.py | 2 +- oioioi/plagiarism/utils.py | 25 ++---- oioioi/portals/forms.py | 4 +- oioioi/portals/models.py | 4 +- oioioi/portals/tests.py | 10 +-- oioioi/portals/widgets.py | 10 +-- oioioi/printing/forms.py | 4 +- oioioi/printing/pdf.py | 2 +- oioioi/problems/admin.py | 44 +++++----- oioioi/problems/controllers.py | 4 +- oioioi/problems/forms.py | 24 +++--- .../management/commands/addproblem.py | 2 +- .../commands/create_mock_competition.py | 14 +-- .../management/commands/mass_create_tool.py | 7 +- oioioi/problems/models.py | 38 +++------ oioioi/problems/package.py | 2 +- oioioi/problems/problem_site.py | 6 +- oioioi/problems/tests/test_problemset.py | 4 +- oioioi/problems/tests/utilities.py | 2 +- oioioi/problems/views.py | 6 +- oioioi/problemsharing/forms.py | 2 +- oioioi/problemsharing/views.py | 2 +- oioioi/programs/admin.py | 36 ++++---- oioioi/programs/controllers.py | 41 +++++---- oioioi/programs/forms.py | 4 +- oioioi/programs/handlers.py | 33 ++++--- oioioi/programs/models.py | 14 ++- oioioi/programs/templatetags/runtimeformat.py | 4 +- oioioi/programs/tests.py | 32 +++---- oioioi/programs/utils.py | 4 +- oioioi/programs/widgets.py | 4 +- oioioi/publicsolutions/controllers.py | 2 +- oioioi/publicsolutions/forms.py | 2 +- oioioi/publicsolutions/tests.py | 6 +- oioioi/questions/admin.py | 18 ++-- oioioi/questions/controllers.py | 2 +- oioioi/questions/forms.py | 14 +-- oioioi/questions/models.py | 6 +- oioioi/questions/tests.py | 10 +-- oioioi/questions/utils.py | 8 +- oioioi/questions/views.py | 13 ++- oioioi/quizzes/admin.py | 8 +- oioioi/quizzes/controllers.py | 6 +- oioioi/quizzes/problem_sources.py | 2 +- oioioi/quizzes/views.py | 6 +- oioioi/rankings/controllers.py | 4 +- oioioi/rankings/forms.py | 2 +- oioioi/rankings/tests.py | 12 +-- oioioi/rankings/views.py | 2 +- oioioi/scoresreveal/admin.py | 10 +-- oioioi/scoresreveal/controllers.py | 8 +- oioioi/similarsubmits/admin.py | 29 +++---- oioioi/similarsubmits/controllers.py | 18 ++-- oioioi/similarsubmits/forms.py | 25 +++--- oioioi/similarsubmits/tests.py | 5 +- oioioi/similarsubmits/views.py | 2 +- oioioi/simpleui/forms.py | 4 +- oioioi/simpleui/tests.py | 2 +- oioioi/sinolpack/admin.py | 2 +- oioioi/sinolpack/controllers.py | 8 +- oioioi/sinolpack/package.py | 44 +++++----- oioioi/sinolpack/tests.py | 20 ++--- oioioi/sinolpack/utils.py | 4 +- oioioi/sioworkers/backends.py | 6 +- .../sioworkers/management/commands/worker.py | 6 +- oioioi/sioworkers/tests.py | 10 +-- oioioi/statistics/admin.py | 2 +- oioioi/statistics/plotfunctions.py | 14 +-- oioioi/statistics/plottypes.py | 14 +-- oioioi/statistics/views.py | 6 +- oioioi/su/forms.py | 4 +- oioioi/su/tests.py | 2 +- oioioi/submitservice/controllers.py | 2 +- .../static/submitservice/submit.py | 16 ++-- oioioi/suspendjudge/admin.py | 6 +- oioioi/suspendjudge/controllers.py | 4 +- oioioi/suspendjudge/tests.py | 2 +- oioioi/szkopul/admin.py | 2 +- oioioi/szkopul/controllers.py | 6 +- oioioi/szkopul/forms.py | 4 +- oioioi/szkopul/models.py | 2 +- oioioi/szkopul/tests.py | 2 +- oioioi/teachers/admin.py | 16 ++-- oioioi/teachers/controllers.py | 4 +- oioioi/teachers/forms.py | 6 +- oioioi/teachers/models.py | 4 +- oioioi/teachers/utils.py | 1 - oioioi/teams/admin.py | 6 +- oioioi/teams/controllers.py | 6 +- oioioi/teams/forms.py | 2 +- oioioi/teams/models.py | 4 +- oioioi/testrun/admin.py | 4 +- oioioi/testrun/controllers.py | 26 +++--- oioioi/testrun/handlers.py | 6 +- oioioi/testrun/models.py | 8 +- oioioi/testspackages/admin.py | 6 +- oioioi/testspackages/forms.py | 4 +- oioioi/testspackages/tests.py | 2 +- oioioi/testspackages/views.py | 2 +- oioioi/timeline/tests.py | 4 +- oioioi/timeline/views.py | 4 +- oioioi/usercontests/admin.py | 16 ++-- oioioi/usercontests/controllers.py | 6 +- oioioi/usercontests/forms.py | 4 +- oioioi/usergroups/admin.py | 8 +- oioioi/usergroups/controllers.py | 8 +- oioioi/usergroups/models.py | 2 +- oioioi/usergroups/views.py | 14 +-- oioioi/workers/views.py | 2 +- oioioi/zeus/admin.py | 4 +- oioioi/zeus/backends.py | 18 ++-- oioioi/zeus/controllers.py | 24 +++--- oioioi/zeus/forms.py | 2 +- oioioi/zeus/handlers.py | 14 +-- oioioi/zeus/models.py | 6 +- oioioi/zeus/package.py | 4 +- oioioi/zeus/problem_sources.py | 4 +- pyproject.toml | 21 +---- rst/source/conf.py | 1 - rst/source/gendoc.py | 6 +- setup.py | 6 +- test_setup.py | 4 +- 249 files changed, 1092 insertions(+), 1181 deletions(-) diff --git a/conftest.py b/conftest.py index 10f6cb198..e5ea66eb4 100644 --- a/conftest.py +++ b/conftest.py @@ -1,10 +1,7 @@ -from __future__ import print_function - import pytest - +from django.conf import settings from oioioi.base.tests import pytest_plugin as base_plugin from oioioi.contests.tests import pytest_plugin as contests_plugin -from django.conf import settings def pytest_addoption(parser): diff --git a/ez_setup.py b/ez_setup.py index d6f8ab522..455c5d505 100644 --- a/ez_setup.py +++ b/ez_setup.py @@ -14,8 +14,6 @@ This file can also be run as a script to install or upgrade setuptools. """ -from __future__ import print_function - import os import sys @@ -25,7 +23,7 @@ from md5 import md5 DEFAULT_VERSION = "0.6c11" -DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] +DEFAULT_URL = f"http://pypi.python.org/packages/{sys.version[:3]}/s/setuptools/" md5_data = { "setuptools-0.6b1-py2.3.egg": "8822caf901250d848b996b7f25c6e6ca", @@ -78,7 +76,7 @@ def _validate_md5(egg_name, data): digest = md5(data).hexdigest() if digest != md5_data[egg_name]: print( - ("md5 validation of %s failed! (Possible download problem?)" % egg_name), + (f"md5 validation of {egg_name} failed! (Possible download problem?)"), file=sys.stderr, ) sys.exit(2) @@ -122,12 +120,11 @@ def do_download(): if was_imported: print( ( - "The required version of setuptools (>=%s) is not available, and\n" + f"The required version of setuptools (>={version}) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first, using 'easy_install -U setuptools'." - "\n\n(Currently using %r)" - ) - % (version, e.args[0]), + f"\n\n(Currently using {e.args[0]!r})" + ), file=sys.stderr, ) sys.exit(2) @@ -148,7 +145,7 @@ def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_d """ import urllib.request - egg_name = "setuptools-%s-py%s.egg" % (version, sys.version[:3]) + egg_name = f"setuptools-{version}-py{sys.version[:3]}.egg" url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None @@ -251,7 +248,7 @@ def update_md5(filenames): md5_data[base] = md5(f.read()).hexdigest() f.close() - data = [" %r: %r,\n" % it for it in md5_data.items()] + data = [" {!r}: {!r},\n".format(*it) for it in md5_data.items()] data.sort() repl = "".join(data) diff --git a/oioioi/__init__.py b/oioioi/__init__.py index a8063f0ac..5470ef493 100644 --- a/oioioi/__init__.py +++ b/oioioi/__init__.py @@ -1,2 +1,2 @@ # apply monkey patch -from oioioi.contests import current_contest +from oioioi.contests import current_contest # noqa: F401 diff --git a/oioioi/acm/controllers.py b/oioioi/acm/controllers.py index 537f8a45d..aa5f25a3d 100644 --- a/oioioi/acm/controllers.py +++ b/oioioi/acm/controllers.py @@ -66,7 +66,7 @@ def fill_evaluation_environ(self, environ, submission): environ["score_aggregator"] = "oioioi.acm.utils.acm_score_aggregator" environ["report_kinds"] = ["FULL"] - super(ACMContestController, self).fill_evaluation_environ(environ, submission) + super().fill_evaluation_environ(environ, submission) def update_report_statuses(self, submission, queryset): if submission.kind == "TESTRUN": @@ -77,7 +77,7 @@ def update_report_statuses(self, submission, queryset): def update_submission_score(self, submission): if submission.kind == "TESTRUN": - super(ACMContestController, self).update_submission_score(submission) + super().update_submission_score(submission) return try: report = SubmissionReport.objects.get(submission=submission, status="ACTIVE", kind="FULL") @@ -111,7 +111,7 @@ def __init__(self, user): def _fill_user_result_for_problem(self, result, pi_submissions): if pi_submissions: - for penalties_count, submission in enumerate(pi_submissions, 1): + for penalties_count, submission in enumerate(pi_submissions, 1): # noqa: B007 if submission.status == "IGN": # We have found IGNORED submission before accepted one. # This means, that some other @@ -213,7 +213,7 @@ def can_submit(self, request, problem_instance, check_round_times=True): return True if not is_participant(request): return False - return super(ACMOpenContestController, self).can_submit(request, problem_instance, check_round_times) + return super().can_submit(request, problem_instance, check_round_times) class _FakeUserResultForProblem: diff --git a/oioioi/acm/score.py b/oioioi/acm/score.py index 5873b4d92..654f5dd76 100644 --- a/oioioi/acm/score.py +++ b/oioioi/acm/score.py @@ -7,7 +7,7 @@ def format_time(seconds): minutes = seconds // 60 - return "%d:%02d" % (minutes // 60, minutes % 60) + return f"{minutes // 60}:{minutes % 60:02d}" @total_ordering @@ -143,14 +143,10 @@ def penalty_repr(self): if self.penalties_count <= 3: return "*" * self.penalties_count else: - return "*(%d)" % (self.penalties_count,) + return f"*({self.penalties_count})" def total_time_repr(self): - return "%d:%02d:%02d" % ( - self.total_time / 3600, - (self.total_time % 3600) / 60, - self.total_time % 60, - ) + return f"{int(self.total_time / 3600)}:{int((self.total_time % 3600) / 60):02d}:{int(self.total_time % 60):02d}" def time_passed_repr(self): return format_time(self.time_passed) @@ -182,13 +178,7 @@ def _to_repr(self): ``penalties_count`` is number of unsuccessful submissions. """ ordering = 10**10 * (self.problems_solved + 1) - self.total_time - return "%020d:%010d:%010d:%010d:%010d" % ( - ordering, - self.problems_solved, - self.time_passed, - self.penalties_count, - self.penalty_time, - ) + return f"{int(ordering):020d}:{self.problems_solved:010d}:{self.time_passed:010d}:{self.penalties_count:010d}:{self.penalty_time:010d}" @property def total_time(self): diff --git a/oioioi/amppz/controllers.py b/oioioi/amppz/controllers.py index 478990e6e..f4edd7932 100644 --- a/oioioi/amppz/controllers.py +++ b/oioioi/amppz/controllers.py @@ -41,7 +41,7 @@ def default_can_see_ranking(self, request): return is_contest_admin(request) or is_contest_observer(request) def default_contestlogo_url(self): - return "%samppz/images/logo-cropped.png" % settings.STATIC_URL + return f"{settings.STATIC_URL}amppz/images/logo-cropped.png" def default_contesticons_urls(self): - return ["%samppz/images/menu-icon.png" % settings.STATIC_URL] + return [f"{settings.STATIC_URL}amppz/images/menu-icon.png"] diff --git a/oioioi/balloons/admin.py b/oioioi/balloons/admin.py index d985be620..273b63290 100644 --- a/oioioi/balloons/admin.py +++ b/oioioi/balloons/admin.py @@ -45,7 +45,7 @@ def color_display(self, instance): color_display.short_description = _("Color") def get_queryset(self, request): - qs = super(ProblemBalloonsConfigAdmin, self).get_queryset(request) + qs = super().get_queryset(request) return qs.filter(problem_instance__contest=request.contest) def formfield_for_foreignkey(self, db_field, request, **kwargs): @@ -54,7 +54,7 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): if request.contest: qs = qs.filter(contest=request.contest) kwargs["queryset"] = qs - return super(ProblemBalloonsConfigAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) contest_site.contest_register(ProblemBalloonsConfig, ProblemBalloonsConfigAdmin) @@ -85,7 +85,7 @@ def get_readonly_fields(self, request, obj=None): return self.readonly_fields def get_queryset(self, request): - qs = super(BalloonsDisplayAdmin, self).get_queryset(request) + qs = super().get_queryset(request) if request.contest is None: return qs return qs.filter(contest=request.contest) @@ -101,7 +101,7 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): if qs or not request.user.is_superuser: kwargs["queryset"] = qs - return super(BalloonsDisplayAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) admin.site.register(BalloonsDisplay, BalloonsDisplayAdmin) @@ -159,7 +159,7 @@ class BalloonsDeliveryAccessDataAdminMixin: """ def __init__(self, *args, **kwargs): - super(BalloonsDeliveryAccessDataAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (BalloonsDeliveryAccessDataInline,) diff --git a/oioioi/balloons/controllers.py b/oioioi/balloons/controllers.py index 2be26bc2e..fd053be94 100644 --- a/oioioi/balloons/controllers.py +++ b/oioioi/balloons/controllers.py @@ -16,7 +16,7 @@ class BalloonsDeliveryACMControllerMixin: """ def submission_judged(self, submission, rejudged=False): - super(BalloonsDeliveryACMControllerMixin, self).submission_judged(submission, rejudged) + super().submission_judged(submission, rejudged) self._create_balloon_delivery(submission) @transaction.atomic diff --git a/oioioi/balloons/models.py b/oioioi/balloons/models.py index c3e3783b1..1888a1aaf 100644 --- a/oioioi/balloons/models.py +++ b/oioioi/balloons/models.py @@ -56,11 +56,11 @@ class Meta: ordering = ["id"] def __str__(self): - return "%(user)s for %(problem)s (%(delivered)s)" % { - "user": self.user, - "problem": self.problem_instance, - "delivered": "delivered" if self.delivered else "not delivered", - } + return "{user} for {problem} ({delivered})".format( + user=self.user, + problem=self.problem_instance, + delivered="delivered" if self.delivered else "not delivered", + ) class BalloonsDeliveryAccessData(models.Model): diff --git a/oioioi/balloons/views.py b/oioioi/balloons/views.py index 710fe30fc..daac60533 100644 --- a/oioioi/balloons/views.py +++ b/oioioi/balloons/views.py @@ -24,7 +24,7 @@ @make_request_condition def has_balloons_cookie(request): try: - key = request.COOKIES["balloons_access_%s" % request.contest.id] + key = request.COOKIES[f"balloons_access_{request.contest.id}"] access_data = BalloonsDeliveryAccessData.objects.get(contest__id=request.contest.id, access_key=key) except (KeyError, BalloonsDeliveryAccessData.DoesNotExist): return False @@ -115,7 +115,7 @@ def balloons_access_cookie_view(request, access_key): access_data.save() response = redirect("balloons_delivery_panel", contest_id=request.contest.id) response.set_cookie( - key="balloons_access_%s" % request.contest.id, + key=f"balloons_access_{request.contest.id}", value=access_key, expires=validity_date, ) diff --git a/oioioi/base/admin.py b/oioioi/base/admin.py index 5b366bf3b..61ab25f34 100644 --- a/oioioi/base/admin.py +++ b/oioioi/base/admin.py @@ -74,10 +74,10 @@ def response_change(self, request, obj): return HttpResponseRedirect(request.get_full_path()) if "came_from" in request.GET and "_continue" not in request.POST and "_saveasnew" not in request.POST and "_addanother" not in request.POST: return safe_redirect(request, request.GET.get("came_from")) - return super(ModelAdmin, self).response_change(request, obj) + return super().response_change(request, obj) def change_view(self, request, object_id, form_url="", extra_context=None): - response = super(ModelAdmin, self).change_view(request, object_id, form_url, extra_context) + response = super().change_view(request, object_id, form_url, extra_context) if isinstance(response, TemplateResponse) and "came_from" in request.GET: response.context_data["form_url"] += "?" + urllib.parse.urlencode({"came_from": request.GET.get("came_from")}) return response @@ -90,7 +90,7 @@ def response_delete(self, request): return HttpResponseRedirect(reverse("admin:index", current_app=self.admin_site.name)) return HttpResponseRedirect( reverse( - "admin:%s_%s_changelist" % (opts.app_label, opts.model_name), + f"admin:{opts.app_label}_{opts.model_name}_changelist", current_app=self.admin_site.name, ) ) @@ -134,8 +134,8 @@ def delete_view(self, request, object_id, extra_context=None): request, self.delete_confirmation_template or [ - "admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()), - "admin/%s/delete_confirmation.html" % app_label, + f"admin/{app_label}/{opts.object_name.lower()}/delete_confirmation.html", + f"admin/{app_label}/delete_confirmation.html", "admin/delete_confirmation.html", ], context, @@ -149,7 +149,7 @@ def get_custom_list_select_related(self): return [] def get_queryset(self, request): - qs = super(ModelAdmin, self).get_queryset(request) + qs = super().get_queryset(request) list_select_related = self.get_custom_list_select_related() if list_select_related: return qs.select_related(*list_select_related) @@ -238,8 +238,8 @@ def delete_selected(modeladmin, request, queryset, **kwargs): request, custom_template or [ - "admin/%s/%s/delete_selected_confirmation.html" % (app_label, opts.model_name), - "admin/%s/delete_selected_confirmation.html" % app_label, + f"admin/{app_label}/{opts.model_name}/delete_selected_confirmation.html", + f"admin/{app_label}/delete_selected_confirmation.html", "admin/delete_selected_confirmation.html", ], context, @@ -274,7 +274,7 @@ def format_callback(obj): if not request.user.is_superuser and not model_admin.has_delete_permission(request, obj): perms_needed.add(opts.verbose_name) - return "%s: %s" % (capfirst(force_str(opts.verbose_name)), force_str(obj)) + return f"{capfirst(force_str(opts.verbose_name))}: {force_str(obj)}" # Get a nested list of dependent objects to_delete = collector.nested(format_callback) @@ -286,7 +286,7 @@ def format_callback(obj): class AdminSite(DjangoAdminSite): def __init__(self, *args, **kwargs): - super(AdminSite, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Override default delete_selected action handler # See delete_selected() docstring for further information self._actions["delete_selected"] = delete_selected @@ -303,7 +303,7 @@ def _reinit_model_admins(self): def get_urls(self): self._reinit_model_admins() - return super(AdminSite, self).get_urls() + return super().get_urls() def login(self, request, extra_context=None): next_url = request.GET.get("next", None) @@ -427,7 +427,7 @@ def has_delete_permission(self, request, obj=None): class UserWithConsentsAdminMixin: def __init__(self, *args, **kwargs): - super(UserWithConsentsAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (ConsentsInline,) diff --git a/oioioi/base/fields.py b/oioioi/base/fields.py index 4da370552..9c38a9549 100644 --- a/oioioi/base/fields.py +++ b/oioioi/base/fields.py @@ -45,7 +45,7 @@ def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_ _choices = self._generate_choices() # pylint: disable=W0125 choices = list(_choices) if _choices else [] - named_groups = choices and isinstance(choices[0][1], (list, tuple)) + named_groups = choices and isinstance(choices[0][1], list | tuple) if not named_groups: for choice, __ in choices: if choice in ("", None): @@ -93,7 +93,7 @@ def validate(self, value, model_instance): superclass = self._get_superclass() if not issubclass(obj, superclass): - raise ValidationError(_("%(value)s is not a %(class_name)s") % dict(value=value, class_name=superclass.__name__)) + raise ValidationError(_("%(value)s is not a %(class_name)s") % {"value": value, "class_name": superclass.__name__}) if getattr(obj, "abstract", False): raise ValidationError(_("%s is an abstract class and cannot be used") % (value,)) @@ -107,11 +107,11 @@ def validate(self, value, model_instance): if _choices and value not in self.empty_values: for option_key, option_value in _choices: - if isinstance(option_value, (list, tuple)): + if isinstance(option_value, list | tuple): # This is an optgroup, so look inside the group for # options. # pylint: disable=W0612 - for optgroup_key, optgroup_value in option_value: + for optgroup_key, _optgroup_value in option_value: if value == optgroup_key: return elif value == option_key: @@ -139,17 +139,17 @@ def _generate_choices(self): subclasses = superclass.subclasses if subclasses: for subclass in subclasses: - dotted_name = "%s.%s" % (subclass.__module__, subclass.__name__) + dotted_name = f"{subclass.__module__}.{subclass.__name__}" human_readable_name = getattr(subclass, "description", dotted_name) yield dotted_name, human_readable_name def to_python(self, value): superclass = self._get_superclass() superclass.load_subclasses() - return super(DottedNameField, self).to_python(value) + return super().to_python(value) def deconstruct(self): - name, path, args, kwargs = super(DottedNameField, self).deconstruct() + name, path, args, kwargs = super().deconstruct() kwargs["superclass"] = self.superclass_name del kwargs["max_length"] return name, path, args, kwargs @@ -175,7 +175,7 @@ def __getitem__(self, key): def register(self, value, description): if len(value) > self.max_length: - raise ValueError("Enum values must not be longer than %d chars" % (self.max_length,)) + raise ValueError(f"Enum values must not be longer than {self.max_length} chars") if not self.entries or value not in next(zip(*self.entries, strict=False)): self.entries.append((value, description)) @@ -210,7 +210,7 @@ def __init__(self, registry=None, *args, **kwargs): # This allows this field to be stored for migration purposes # without the need to serialize an EnumRegistry object. # Instead, we serialize 'max_length' and 'choices'. - assert isinstance(registry, EnumRegistry), "Invalid registry passed to EnumField.__init__: %r" % (registry,) + assert isinstance(registry, EnumRegistry), f"Invalid registry passed to EnumField.__init__: {registry!r}" kwargs["max_length"] = registry.max_length kwargs["choices"] = self._generate_choices() models.CharField.__init__(self, *args, **kwargs) @@ -219,7 +219,7 @@ def _generate_choices(self): return list(self.registry.entries) def deconstruct(self): - name, path, args, kwargs = super(EnumField, self).deconstruct() + name, path, args, kwargs = super().deconstruct() kwargs.pop("choices", None) return name, path, args, kwargs @@ -243,10 +243,10 @@ def __init__(self, *args, **kwargs): kwargs["validators"] = [RegexValidator(r"^\+?[0-9() -]{6,}$", _("Invalid phone number"))] kwargs["help_text"] = _("Including the area code.") - super(PhoneNumberField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def deconstruct(self): - name, path, args, kwargs = super(PhoneNumberField, self).deconstruct() + name, path, args, kwargs = super().deconstruct() del kwargs["max_length"] del kwargs["validators"] del kwargs["help_text"] @@ -260,10 +260,10 @@ def __init__(self, *args, **kwargs): kwargs["max_length"] = 6 kwargs["validators"] = [RegexValidator(r"^\d{2}-\d{3}$", _("Enter a postal code in the format XX-XXX"))] - super(PostalCodeField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def deconstruct(self): - name, path, args, kwargs = super(PostalCodeField, self).deconstruct() + name, path, args, kwargs = super().deconstruct() del kwargs["max_length"] del kwargs["validators"] return name, path, args, kwargs diff --git a/oioioi/base/forms.py b/oioioi/base/forms.py index 2021a9e25..885e1f0ff 100644 --- a/oioioi/base/forms.py +++ b/oioioi/base/forms.py @@ -178,7 +178,7 @@ class Media: def __init__(self, *args, **kwargs): extra = kwargs.pop("extra", {}) - super(RegistrationFormWithNames, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) adjust_username_field(self) tmp_fields = list(self.fields.items()) tmp_fields[1:1] = [ @@ -200,7 +200,7 @@ def __init__(self, *args, **kwargs): self.allow_login_change = kwargs.pop("allow_login_change", False) extra = kwargs.pop("extra", {}) - super(UserForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) adjust_username_field(self) adjust_name_fields(self) @@ -220,7 +220,7 @@ def clean_username(self): return self.cleaned_data["username"] def save(self, *args, **kwargs): - instance = super(UserForm, self).save(*args, **kwargs) + instance = super().save(*args, **kwargs) PreferencesSaved.send(self, user=instance) return instance @@ -232,7 +232,7 @@ class Media: js = ("js/email-change.js",) def __init__(self, *args, **kwargs): - super(OioioiUserForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.user = kwargs.pop("instance", None) def clean_confirm_password(self): @@ -254,13 +254,13 @@ def clean_confirm_password(self): class OioioiUserCreationForm(UserCreationForm): def __init__(self, *args, **kwargs): - super(OioioiUserCreationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) adjust_username_field(self) class OioioiUserChangeForm(UserChangeForm): def __init__(self, *args, **kwargs): - super(OioioiUserChangeForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) adjust_username_field(self) adjust_name_fields(self) self.fields["user_permissions"].queryset = Permission.objects.filter(codename="can_modify_tags") @@ -344,7 +344,7 @@ def tag_as_str(self, tag): return tag def __init__(self, request, *args, **kwargs): - super(PublicMessageForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["content"].widget.attrs["class"] = "monospace" self.contest = request.contest self.fields["content"].help_text = _("You can use the following tags and attributes: {}.").format( @@ -360,7 +360,7 @@ def clean_content(self): ) def save(self, commit=True, *args, **kwargs): - instance = super(PublicMessageForm, self).save(commit=False, *args, **kwargs) + instance = super().save(commit=False, *args, **kwargs) instance.contest = self.contest if commit: instance.save() diff --git a/oioioi/base/management/commands/migrate.py b/oioioi/base/management/commands/migrate.py index 112f42958..e4d4e7903 100644 --- a/oioioi/base/management/commands/migrate.py +++ b/oioioi/base/management/commands/migrate.py @@ -28,9 +28,9 @@ def handle(self, *args, **options): "based on version 1.5 or 1.6 of the " "Django framework. You'll have to make " "an extra step before syncing your " - "database. Consult %s for " - "instructions." % GITHUB_LINK + f"database. Consult {GITHUB_LINK} for " + "instructions." ) return - super(Command, self).handle(*args, **options) + super().handle(*args, **options) diff --git a/oioioi/base/management/commands/shell.py b/oioioi/base/management/commands/shell.py index 0813bef88..40dc85aeb 100644 --- a/oioioi/base/management/commands/shell.py +++ b/oioioi/base/management/commands/shell.py @@ -98,7 +98,7 @@ def execute(self, sql, params=()): else: print(raw_sql) print() - print("Execution time: %.6fs [Database: %s]" % (execution_time, self.db.alias)) + print(f"Execution time: {execution_time:.6f}s [Database: {self.db.alias}]") print() util.CursorDebugWrapper = PrintQueryWrapper diff --git a/oioioi/base/menu.py b/oioioi/base/menu.py index df8c6ad55..ae4fc7d39 100644 --- a/oioioi/base/menu.py +++ b/oioioi/base/menu.py @@ -92,7 +92,10 @@ def _get_all_registered_items(self, request): def __init__(self, text=None, condition=None, show_icons=False): self.text = text if condition is None: - condition = lambda request: True + + def condition(request): + return True + self.condition = condition self.show_icons = show_icons self._registry = [] @@ -175,15 +178,15 @@ def template_context(self, request): context_items = [] for item in sorted(items, key=attrgetter("order")): if item.condition(request): - attrs_str = " ".join(['%s="%s"' % (escape(k), escape(v)) for (k, v) in item.attrs.items()]) + attrs_str = " ".join([f'{escape(k)}="{escape(v)}"' for (k, v) in item.attrs.items()]) attrs_str = mark_safe(attrs_str) context_items.append( - dict( - url=item.url_generator(request), - text=item.text, - attrs=attrs_str, - has_icon=self.show_icons, - ) + { + "url": item.url_generator(request), + "text": item.text, + "attrs": attrs_str, + "has_icon": self.show_icons, + } ) return context_items diff --git a/oioioi/base/models.py b/oioioi/base/models.py index 7c12a2ba8..a129f2585 100644 --- a/oioioi/base/models.py +++ b/oioioi/base/models.py @@ -1,5 +1,12 @@ +import logging + import django.dispatch from django.conf import settings +from django.contrib.auth.models import User +from django.db import models +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.utils.translation import gettext_lazy as _ # pylint: disable=unused-import # Important. This import is to register signal handlers. Do not remove it. @@ -13,13 +20,6 @@ setup_check() captcha_check() -import logging - -from django.contrib.auth.models import User -from django.db import models -from django.db.models.signals import post_save -from django.dispatch import receiver -from django.utils.translation import gettext_lazy as _ auditLogger = logging.getLogger(__name__ + ".audit") diff --git a/oioioi/base/permissions.py b/oioioi/base/permissions.py index e4bf7246b..436538bf9 100644 --- a/oioioi/base/permissions.py +++ b/oioioi/base/permissions.py @@ -35,7 +35,7 @@ class Condition: """ def __init__(self, condition, *args, **kwargs): - super(Condition, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.condition = condition def __call__(self, *args, **kwargs): @@ -44,17 +44,25 @@ def __call__(self, *args, **kwargs): def __or__(self, other): if not isinstance(other, Condition): return NotImplemented - condition_or = lambda *args, **kwargs: self(*args, **kwargs) or other(*args, **kwargs) + + def condition_or(*args, **kwargs): + return self(*args, **kwargs) or other(*args, **kwargs) + return Condition(condition_or) def __and__(self, other): if not isinstance(other, Condition): return NotImplemented - condition_and = lambda *args, **kwargs: self(*args, **kwargs) and other(*args, **kwargs) + + def condition_and(*args, **kwargs): + return self(*args, **kwargs) and other(*args, **kwargs) + return Condition(condition_and) def __invert__(self): - condition_inverted = lambda *args, **kwargs: not self(*args, **kwargs) + def condition_inverted(*args, **kwargs): + return not self(*args, **kwargs) + return Condition(condition_inverted) diff --git a/oioioi/base/templatetags/all_with_prefix.py b/oioioi/base/templatetags/all_with_prefix.py index 99ff81be9..ed5251c75 100644 --- a/oioioi/base/templatetags/all_with_prefix.py +++ b/oioioi/base/templatetags/all_with_prefix.py @@ -24,6 +24,6 @@ def all_with_prefix(parser, token): try: _tag_name, prefix = token.split_contents() except ValueError: - msg = "%r tag requires a single argument" % token.split_contents()[0] + msg = f"{token.split_contents()[0]!r} tag requires a single argument" raise TemplateSyntaxError(msg) return AllWithPrefixNode(prefix) diff --git a/oioioi/base/templatetags/common_media.py b/oioioi/base/templatetags/common_media.py index 42714f058..93c6ba9ad 100644 --- a/oioioi/base/templatetags/common_media.py +++ b/oioioi/base/templatetags/common_media.py @@ -14,9 +14,9 @@ def generate_styles(): lines = [] for path in find_common_media(): if path.endswith(".css"): - lines.append('' % (path,)) + lines.append(f'') elif path.endswith(".scss"): - lines.append('' % (path,)) + lines.append(f'') return "\n".join(lines) @@ -24,7 +24,7 @@ def generate_scripts(): lines = [] for path in find_common_media(): if path.endswith(".js"): - lines.append('' % (path,)) + lines.append(f'') return "\n".join(lines) diff --git a/oioioi/base/templatetags/menu.py b/oioioi/base/templatetags/menu.py index 2be6b4794..19a716128 100644 --- a/oioioi/base/templatetags/menu.py +++ b/oioioi/base/templatetags/menu.py @@ -19,7 +19,7 @@ def render(self, context): if isinstance(registry, str): registry = import_string(registry) if not isinstance(registry, MenuRegistry): - raise TemplateSyntaxError("{%% generate_menu %%} got an argument which is not a MenuRegistry: %r" % (registry,)) + raise TemplateSyntaxError(f"{{% generate_menu %}} got an argument which is not a MenuRegistry: {registry!r}") context["menu"] = registry.template_context(request) return "" @@ -64,7 +64,7 @@ def generate_menu(parser, token): """ bits = token.split_contents() if len(bits) > 2: - raise TemplateSyntaxError("Unexpected arguments to {%% %s %%}" % (bits[0],)) + raise TemplateSyntaxError(f"Unexpected arguments to {{% {bits[0]} %}}") if len(bits) == 2: target = parser.compile_filter(bits[1]) return GenerateMenuNode(target) diff --git a/oioioi/base/templatetags/simple_filters.py b/oioioi/base/templatetags/simple_filters.py index 121e9ae06..9e1329d79 100644 --- a/oioioi/base/templatetags/simple_filters.py +++ b/oioioi/base/templatetags/simple_filters.py @@ -219,7 +219,7 @@ def json_parse(value): """ This is a correct way of embedding json inside js in an HTML template. """ - return mark_safe("JSON.parse('%s')" % escapejs(json.dumps(value))) + return mark_safe(f"JSON.parse('{escapejs(json.dumps(value))}')") @register.filter diff --git a/oioioi/base/tests/__init__.py b/oioioi/base/tests/__init__.py index 4d2ef81fa..f49b71646 100644 --- a/oioioi/base/tests/__init__.py +++ b/oioioi/base/tests/__init__.py @@ -5,11 +5,10 @@ from unittest import mock import pytest -from django.contrib.auth.models import AnonymousUser, User +from django.contrib.auth.models import User from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured from django.db import DEFAULT_DB_ALIAS, connections -from django.template.loaders.cached import Loader as CachedLoader from django.test import TestCase as DjangoTestCase from django.test.utils import CaptureQueriesContext from django.urls import reverse @@ -27,16 +26,16 @@ class _AssertNumQueriesLessThanContext(CaptureQueriesContext): def __init__(self, test_case, num, connection): self.test_case = test_case self.num = num - super(_AssertNumQueriesLessThanContext, self).__init__(connection) + super().__init__(connection) def __exit__(self, exc_type, exc_value, traceback): - super(_AssertNumQueriesLessThanContext, self).__exit__(exc_type, exc_value, traceback) + super().__exit__(exc_type, exc_value, traceback) if exc_type is not None: return executed = len(self) self.test_case.assertTrue( executed < self.num, - "%d queries executed, expected less than %d" % (executed, self.num), + f"{executed} queries executed, expected less than {self.num}", ) @@ -86,7 +85,7 @@ def authenticate(self, request, username=None, password=None, **kwargs): return User.objects.get(username=username) except User.DoesNotExist: raise AssertionError( - "Tried to log in as %r without password, but such a user does not exist. Probably the test forgot to import a database fixture." % (username,) + f"Tried to log in as {username!r} without password, but such a user does not exist. Probably the test forgot to import a database fixture." ) def get_user(self, user_id): diff --git a/oioioi/base/tests/tests.py b/oioioi/base/tests/tests.py index a419654e4..2ec2b660b 100644 --- a/oioioi/base/tests/tests.py +++ b/oioioi/base/tests/tests.py @@ -91,8 +91,8 @@ def test_check_perms(self): admin = User.objects.get(username="test_admin") user = User.objects.get(username="test_user") template = Template('{% load check_perm %}{% check_perm "auth.add_user" for "whatever" as p %}{% if p %}yes{% endif %}') - self.assertEqual(template.render(Context(dict(user=admin))), "yes") - self.assertEqual(template.render(Context(dict(user=user))), "") + self.assertEqual(template.render(Context({"user": admin})), "yes") + self.assertEqual(template.render(Context({"user": user})), "") class TestIndex(TestCase): @@ -558,7 +558,7 @@ def test_utils_dont_need_settings(self): class TestAllWithPrefix(TestCase): def test_all_with_prefix(self): t = Template("{% load all_with_prefix %}{% all_with_prefix a_ %}") - context = Context(dict(a_foo="foo", a_bar="bar", b_baz="baz")) + context = Context({"a_foo": "foo", "a_bar": "bar", "b_baz": "baz"}) rendered = t.render(context) self.assertIn("foo", rendered) self.assertIn("bar", rendered) @@ -610,7 +610,7 @@ def setUp(self): def test_basic_usage(self): field = EnumField(self.registry) - self.assertEqual(sorted(list(field.choices)), [("ERR", "Error"), ("OK", "OK")]) + self.assertEqual(sorted(field.choices), [("ERR", "Error"), ("OK", "OK")]) with self.assertRaises(ValidationError): field.validate("FOO", None) @@ -717,7 +717,7 @@ def _reload_urlconf(self): clear_url_caches() def _register_user(self, terms_accepted=True, pass_captcha=True): - response = self.client.get(reverse("sign-up")) + self.client.get(reverse("sign-up")) captcha_count = CaptchaStore.objects.count() self.assertEqual(captcha_count, 1) captcha = CaptchaStore.objects.all()[0] @@ -1232,9 +1232,9 @@ def test_can_change_login_from_invalid(self): # The html strings underneath may change with any django upgrade. self.assertContains( response, - '' % value, + 'aria-describedby="id_username_helptext" id="id_username">', html=True, ) @@ -1265,9 +1265,9 @@ def test_login_cannot_change_from_valid(self): response = self.client.get(self.url_edit_profile) self.assertContains( response, - '' % value, + 'aria-describedby="id_username_helptext" id="id_username">', html=True, ) diff --git a/oioioi/base/utils/__init__.py b/oioioi/base/utils/__init__.py index ece570e9f..fd6178774 100644 --- a/oioioi/base/utils/__init__.py +++ b/oioioi/base/utils/__init__.py @@ -14,8 +14,6 @@ import six from django.forms.utils import flatatt from django.http import Http404, HttpResponse, HttpResponseRedirect -from django.shortcuts import render -from django.template import Template from django.template.loader import render_to_string from django.template.response import TemplateResponse from django.utils.encoding import force_str @@ -30,7 +28,7 @@ class ClassInitMeta(type): """Meta class triggering __classinit__ on class intialization.""" def __init__(cls, class_name, bases, new_attrs): - super(ClassInitMeta, cls).__init__(class_name, bases, new_attrs) + super().__init__(class_name, bases, new_attrs) cls.__classinit__() @@ -96,7 +94,7 @@ def __classinit__(cls): # This is an artificial class created by mixins mechanism return - assert "subclasses" not in cls.__dict__, "%s defines attribute subclasses, but has RegisteredSubclassesMeta metaclass" % (cls,) + assert "subclasses" not in cls.__dict__, f"{cls} defines attribute subclasses, but has RegisteredSubclassesMeta metaclass" cls.subclasses = [] cls.abstract = cls.__dict__.get("abstract", False) @@ -105,7 +103,7 @@ def find_superclass(cls): if not superclasses: return None if len(superclasses) > 1: - raise AssertionError("%s derives from more than one RegisteredSubclassesBase" % (cls.__name__,)) + raise AssertionError(f"{cls.__name__} derives from more than one RegisteredSubclassesBase") superclass = superclasses[0] if "__unmixed_class__" in superclass.__dict__: superclass = superclass.__unmixed_class__ @@ -131,7 +129,7 @@ def load_subclasses(cls): for app_module in list(settings.INSTALLED_APPS): for name in modules_to_load: try: - module = "%s.%s" % (app_module, name) + module = f"{app_module}.{name}" import_module(module) except ImportError: continue @@ -141,7 +139,7 @@ def load_subclasses(cls): class _RemoveMixinsFromInitMixin: def __init__(self, *args, **kwargs): kwargs.pop("mixins", None) - super(_RemoveMixinsFromInitMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) class ObjectWithMixins(ClassInitBase): @@ -255,7 +253,7 @@ def _make_mx_class(cls, mixins): return type( cls.__name__ + "WithMixins", bases, - dict(__module__=cls.__module__, __unmixed_class__=cls), + {"__module__": cls.__module__, "__unmixed_class__": cls}, ) else: return cls @@ -291,9 +289,8 @@ def _fixup_subclass(cls, subclass): def mix_in(cls, mixin): """Appends the given mixin to the list of class mixins.""" assert cls.__unmixed_class__ is cls - assert cls.allow_too_late_mixins or "_has_instances" not in cls.__dict__, "Adding mixin %r to %r too late. The latter already has instances." % ( - mixin, - cls, + assert cls.allow_too_late_mixins or "_has_instances" not in cls.__dict__, ( + f"Adding mixin {mixin!r} to {cls!r} too late. The latter already has instances." ) cls.mixins.append(mixin) cls._mx_class = None @@ -386,7 +383,7 @@ def make_html_link(href, name, method="GET", extra_attrs=None): if not extra_attrs: extra_attrs = {} attrs.update(extra_attrs) - return mark_safe("%s" % (flatatt(attrs), conditional_escape(force_str(name)))) + return mark_safe(f"{conditional_escape(force_str(name))}") def make_html_links(links, extra_attrs=None): @@ -508,7 +505,9 @@ def strip_num_or_hash(filename): def naturalsort_key(key): - convert = lambda text: int(text) if text.isdigit() else text + def convert(text): + return int(text) if text.isdigit() else text + return [convert(c) for c in re.split("([0-9]+)", key)] @@ -524,7 +523,7 @@ def __init__(self, max_value, length=20): def _show(self, preserve=False): done_p = 100 * self.value / self.max_value done_l = self.length * self.value / self.max_value - s = "|" + "=" * done_l + " " * (self.length - done_l) + "| %d%%" % done_p + s = f"|{'=' * done_l}{' ' * (self.length - done_l)}| {done_p}%" self.to_clear = 0 if preserve else len(s) sys.stdout.write(s + ("\n" if preserve else "")) sys.stdout.flush() diff --git a/oioioi/base/utils/archive.py b/oioioi/base/utils/archive.py index df96836df..43e4e4be0 100644 --- a/oioioi/base/utils/archive.py +++ b/oioioi/base/utils/archive.py @@ -104,7 +104,7 @@ def _archive_cls(file, ext=""): base, ext = os.path.splitext(base) cls = extension_map.get(ext) if not cls: - raise UnrecognizedArchiveFormat("Path not a recognized archive format: %s" % filename) + raise UnrecognizedArchiveFormat(f"Path not a recognized archive format: {filename}") return cls def extract(self, *args, **kwargs): @@ -177,7 +177,7 @@ def check_files(self, to_path=None): extract_path = os.path.join(target_path, filename) extract_path = os.path.normpath(os.path.realpath(extract_path)) if not extract_path.startswith(target_path): - raise UnsafeArchive("Archive member destination is outside the target directory. member: %s" % filename) + raise UnsafeArchive(f"Archive member destination is outside the target directory. member: {filename}") class TarArchive(BaseArchive): diff --git a/oioioi/base/utils/color.py b/oioioi/base/utils/color.py index 05af145ff..fc9f15e00 100644 --- a/oioioi/base/utils/color.py +++ b/oioioi/base/utils/color.py @@ -15,13 +15,13 @@ class ColorField(models.CharField): def __init__(self, *args, **kwargs): kwargs["max_length"] = 7 - super(ColorField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def formfield(self, **kwargs): kwargs["widget"] = ColorWidget - return super(ColorField, self).formfield(**kwargs) + return super().formfield(**kwargs) def deconstruct(self): - name, path, args, kwargs = super(ColorField, self).deconstruct() + name, path, args, kwargs = super().deconstruct() del kwargs["max_length"] return name, path, args, kwargs diff --git a/oioioi/base/utils/deps.py b/oioioi/base/utils/deps.py index d6b5477ca..84b9a828b 100644 --- a/oioioi/base/utils/deps.py +++ b/oioioi/base/utils/deps.py @@ -11,16 +11,12 @@ def check_django_app_dependencies(app_name, depends_on, strict=False): app_name = app_name[:-7] if not is_django_app_installed(app_name): raise ImproperlyConfigured( - "Django app %s is loaded (because something depends on it), but it's not in settings.INSTALLED_APPS. Please add it there." % (app_name,) + f"Django app {app_name} is loaded (because something depends on it), but it's not in settings.INSTALLED_APPS. Please add it there." ) index = settings.INSTALLED_APPS.index(app_name) - assert isinstance(depends_on, (list, tuple)) + assert isinstance(depends_on, list | tuple) for dep in depends_on: if not is_django_app_installed(dep): - raise ImproperlyConfigured("Django app %s requires %s, which is not present in settings.INSTALLED_APPS" % (app_name, dep)) + raise ImproperlyConfigured(f"Django app {app_name} requires {dep}, which is not present in settings.INSTALLED_APPS") if strict and index > settings.INSTALLED_APPS.index(dep): - raise ImproperlyConfigured( - "Django app %(overriding)s overrides " - "%(overridden)s, so %(overriding)s should be placed " - "before %(overridden)s in settings.INSTALLED_APPS" % {"overriding": app_name, "overridden": dep} - ) + raise ImproperlyConfigured(f"Django app {app_name} overrides {dep}, so {app_name} should be placed before {dep} in settings.INSTALLED_APPS") diff --git a/oioioi/base/utils/execute.py b/oioioi/base/utils/execute.py index fb177d6cb..a1cebee0f 100644 --- a/oioioi/base/utils/execute.py +++ b/oioioi/base/utils/execute.py @@ -68,7 +68,7 @@ def execute( # Although there is some kind of support for "sequence" commands in the # subprocess module, it works kinda wonky. If you want to investigate, # comment the following two lines and see the tests blow up. - if isinstance(command, (list, tuple)): + if isinstance(command, list | tuple): command = " ".join(quote(x) for x in command) def set_cwd(): @@ -102,6 +102,6 @@ def set_cwd(): if split_lines: stdout = stdout.splitlines() if rc and not ignore_errors and rc not in errors_to_ignore: - raise ExecuteError("Failed to execute command: %s\n%s" % (command, stdout)) + raise ExecuteError(f"Failed to execute command: {command}\n{stdout}") return stdout diff --git a/oioioi/base/utils/input_with_generate.py b/oioioi/base/utils/input_with_generate.py index 7619672b9..94d509f7b 100644 --- a/oioioi/base/utils/input_with_generate.py +++ b/oioioi/base/utils/input_with_generate.py @@ -8,7 +8,7 @@ class TextInputWithGenerate(forms.TextInput): def __init__(self, *args, **kwargs): attrs = kwargs.setdefault("attrs", {}) attrs.setdefault("style", "width:200px;") - super(TextInputWithGenerate, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def render(self, name, value, attrs=None, renderer=None): if self.attrs is not None and "id" in self.attrs: @@ -19,7 +19,7 @@ def render(self, name, value, attrs=None, renderer=None): id = "id" id += "_input-with-generate" - html = super(TextInputWithGenerate, self).render(name, value, attrs, renderer) + html = super().render(name, value, attrs, renderer) html = mark_safe(self.html_template.format(id=id, input_html=html)) return html diff --git a/oioioi/base/utils/loaders.py b/oioioi/base/utils/loaders.py index 0d4d6f0b7..acbe103b7 100644 --- a/oioioi/base/utils/loaders.py +++ b/oioioi/base/utils/loaders.py @@ -7,7 +7,7 @@ def load_modules(module_name): """This function loads module_name.py files in all installed apps.""" for app_module in list(settings.INSTALLED_APPS): try: - module = "%s.%s" % (app_module, module_name) + module = f"{app_module}.{module_name}" import_module(module) except ImportError: continue diff --git a/oioioi/base/utils/user_selection.py b/oioioi/base/utils/user_selection.py index 878688a70..7682feec4 100644 --- a/oioioi/base/utils/user_selection.py +++ b/oioioi/base/utils/user_selection.py @@ -67,7 +67,7 @@ def _get_user_hints(substr, queryset, user_field_name=None): _prefix(user_field_name, "last_name"), ) ) - return ["%s (%s %s)" % u for u in users[:num_hints]] + return ["{} ({} {})".format(*u) for u in users[:num_hints]] @jsonify @@ -109,10 +109,10 @@ def __init__(self, attrs=None): else: attrs = dict(attrs) attrs.setdefault("autocomplete", "off") - super(UserSelectionWidget, self).__init__(attrs) + super().__init__(attrs) def render(self, name, value, attrs=None, renderer=None): - html = super(UserSelectionWidget, self).render(name, value, attrs, renderer) + html = super().render(name, value, attrs, renderer) html += mark_safe( self.html_template % { @@ -127,7 +127,7 @@ class UserSelectionField(forms.CharField): widget = UserSelectionWidget def __init__(self, hints_url=None, queryset=None, user_field_name=None, **kwargs): - super(UserSelectionField, self).__init__(**kwargs) + super().__init__(**kwargs) self.hints_url = hints_url self.queryset = queryset self.user_field_name = user_field_name @@ -148,10 +148,10 @@ def prepare_value(self, value): return User.objects.get(id=value).username except User.DoesNotExist: pass - return super(UserSelectionField, self).prepare_value(value) + return super().prepare_value(value) def to_python(self, value): - value = super(UserSelectionField, self).to_python(value) + value = super().to_python(value) if isinstance(value, User): user = value else: diff --git a/oioioi/base/widgets.py b/oioioi/base/widgets.py index 5d0c08503..a6bf822de 100644 --- a/oioioi/base/widgets.py +++ b/oioioi/base/widgets.py @@ -26,11 +26,11 @@ def get_context(self, name, value, attrs): class AceEditorWidget(forms.widgets.Textarea): def __init__(self, attrs, default_state=False): - super(AceEditorWidget, self).__init__(attrs={"rows": 10, "class": "monospace"}) + super().__init__(attrs={"rows": 10, "class": "monospace"}) self.default_state = default_state def render(self, name, value, attrs=None, renderer=None): - return super(AceEditorWidget, self).render(name, value, attrs=attrs, renderer=renderer) + render_to_string( + return super().render(name, value, attrs=attrs, renderer=renderer) + render_to_string( "widgets/aceeditor.html", { "editor_id": "editor", diff --git a/oioioi/clock/views.py b/oioioi/clock/views.py index 93d10e2e2..3b7b143cf 100644 --- a/oioioi/clock/views.py +++ b/oioioi/clock/views.py @@ -36,13 +36,13 @@ def get_times_status(request, response): timestamp = getattr(request, "timestamp", None) contest = getattr(request, "contest", None) response.update( - dict( - time=0, - round_start_date=0, - round_end_date=0, - is_time_admin=False, - is_admin_time_set=False, - ) + { + "time": 0, + "round_start_date": 0, + "round_end_date": 0, + "is_time_admin": False, + "is_admin_time_set": False, + } ) if getattr(request, "real_user", None) and is_real_superuser(request): diff --git a/oioioi/complaints/admin.py b/oioioi/complaints/admin.py index d818b9fc2..4fad37dcf 100644 --- a/oioioi/complaints/admin.py +++ b/oioioi/complaints/admin.py @@ -26,7 +26,7 @@ class ComplaintsAdminMixin: """ def __init__(self, *args, **kwargs): - super(ComplaintsAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (ComplaintsConfigInline,) diff --git a/oioioi/complaints/views.py b/oioioi/complaints/views.py index e0870c414..bf361797b 100644 --- a/oioioi/complaints/views.py +++ b/oioioi/complaints/views.py @@ -50,7 +50,7 @@ def email_template_context(request, message): participant = Participant.objects.get(user=user, contest=contest) participant_status = participant.get_status_display() try: - participant_status += _(" (%(registration)s)") % dict(registration=participant.registration_model) + participant_status += _(" (%(registration)s)") % {"registration": participant.registration_model} except ObjectDoesNotExist: pass except Participant.DoesNotExist: @@ -61,7 +61,7 @@ def email_template_context(request, message): "user": user, "contest": contest, "message": message.strip(), - "user_info": "%s (%s)" % (user.get_full_name(), user), + "user_info": f"{user.get_full_name()} ({user})", "participant": participant, "participant_status": participant_status, "complaints_email": get_complaints_email(request), @@ -85,8 +85,8 @@ def notify_complainer(request, body, message_id, ref_id): headers={ "Errors-To": context["complaints_email"], "Reply-To": context["complaints_email"], - "Message-ID": "<%s@oioioi>" % message_id, - "References": "<%s@oioioi>" % ref_id, + "Message-ID": f"<{message_id}@oioioi>", + "References": f"<{ref_id}@oioioi>", }, ) message.send() @@ -105,8 +105,8 @@ def notify_jury(request, body, message_id, ref_id): (context["complaints_email"],), headers={ "Reply-To": request.user.email, - "Message-ID": "<%s@oioioi>" % message_id, - "References": "<%s@oioioi>" % ref_id, + "Message-ID": f"<{message_id}@oioioi>", + "References": f"<{ref_id}@oioioi>", }, ) message.send() diff --git a/oioioi/confirmations/controllers.py b/oioioi/confirmations/controllers.py index a11c7daca..8e42ea2cc 100644 --- a/oioioi/confirmations/controllers.py +++ b/oioioi/confirmations/controllers.py @@ -11,7 +11,7 @@ def should_confirm_submission_receipt(self, request, submission): return False def create_submission(self, request, *args, **kwargs): - submission = super(ConfirmationContestControllerMixin, self).create_submission(request, *args, **kwargs) + submission = super().create_submission(request, *args, **kwargs) if self.should_confirm_submission_receipt(request, submission): send_submission_receipt_confirmation(request, submission) diff --git a/oioioi/contestexcl/admin.py b/oioioi/contestexcl/admin.py index da14d8c3b..88431c5b9 100644 --- a/oioioi/contestexcl/admin.py +++ b/oioioi/contestexcl/admin.py @@ -16,7 +16,7 @@ class ExclusivenessConfigInline(admin.TabularInline): category = _("Advanced") def get_fields(self, request, obj=None): - fields = super(ExclusivenessConfigInline, self).get_fields(request, obj) + fields = super().get_fields(request, obj) # Superadmins don't need to see the disable field if obj and request.user.is_superuser: return [f for f in fields if f != "disable"] @@ -49,7 +49,7 @@ class ContestAdminWithExclusivenessInlineMixin: """ def __init__(self, *args, **kwargs): - super(ContestAdminWithExclusivenessInlineMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (ExclusivenessConfigInline,) def _warn_on_contestexcl_overlap(self, request, ex_confs): diff --git a/oioioi/contestexcl/forms.py b/oioioi/contestexcl/forms.py index c0c3372d1..ad23b4072 100644 --- a/oioioi/contestexcl/forms.py +++ b/oioioi/contestexcl/forms.py @@ -18,12 +18,12 @@ class Meta: model = ExclusivenessConfig def clean(self): - super(ExclusivenessConfigForm, self).clean() + super().clean() if self.cleaned_data["disable"] and not self.instance.enabled: raise ValidationError(_("This exclusiveness config is already disabled!")) def save(self, commit=True): - instance = super(ExclusivenessConfigForm, self).save(commit=False) + instance = super().save(commit=False) if self.cleaned_data["disable"]: instance.enabled = False if commit: diff --git a/oioioi/contestexcl/middleware.py b/oioioi/contestexcl/middleware.py index 39fb788ad..83c892352 100644 --- a/oioioi/contestexcl/middleware.py +++ b/oioioi/contestexcl/middleware.py @@ -56,7 +56,9 @@ def _default_selector(user, contest): if selector is None: final_selector = _default_selector else: - final_selector = lambda user, contest: _default_selector(user, contest) and selector(user, contest) + + def final_selector(user, contest): + return _default_selector(user, contest) and selector(user, contest) if settings.ONLY_DEFAULT_CONTEST and (request.user is None or not request.user.is_superuser): qs = [Contest.objects.get(id=settings.DEFAULT_CONTEST)] diff --git a/oioioi/contestexcl/models.py b/oioioi/contestexcl/models.py index da10ffbbd..5d2dfd7ae 100644 --- a/oioioi/contestexcl/models.py +++ b/oioioi/contestexcl/models.py @@ -56,7 +56,7 @@ class Meta: verbose_name_plural = _("exclusiveness configs") def __str__(self): - return "%s (%s): %s - %s" % ( + return "{} ({}): {} - {}".format( self.contest, "enabled" if self.enabled else "disabled", self.start_date, diff --git a/oioioi/contestexcl/tests.py b/oioioi/contestexcl/tests.py index af61a0f31..44bb5bfd9 100644 --- a/oioioi/contestexcl/tests.py +++ b/oioioi/contestexcl/tests.py @@ -191,7 +191,7 @@ def _modify_contestexcl( ("contestcompiler_set", 0, 0, 0, 1000), ("checkerformatforcontest", 0, 0, 0, 1), ) - data = dict() + data = {} for name, total, initial, min_num, max_num in formsets: data.update( { diff --git a/oioioi/contestlogo/admin.py b/oioioi/contestlogo/admin.py index a6d416514..668759ca2 100644 --- a/oioioi/contestlogo/admin.py +++ b/oioioi/contestlogo/admin.py @@ -26,7 +26,7 @@ class ContestLogoAdminMixin: """Adds :class:`~oioioi.contestlogo.models.ContestLogo` to an admin panel.""" def __init__(self, *args, **kwargs): - super(ContestLogoAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (ContestLogoInline,) @@ -59,7 +59,7 @@ class ContestIconAdminMixin: """Adds :class:`~oioioi.contestlogo.models.ContestIcon` to an admin panel.""" def __init__(self, *args, **kwargs): - super(ContestIconAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (ContestIconInline,) diff --git a/oioioi/contestlogo/models.py b/oioioi/contestlogo/models.py index 9410857ec..34feba5e4 100644 --- a/oioioi/contestlogo/models.py +++ b/oioioi/contestlogo/models.py @@ -10,10 +10,7 @@ def make_logo_filename(instance, filename): - return "logo/%s/%s" % ( - instance.contest.id, - get_valid_filename(os.path.basename(filename)), - ) + return f"logo/{instance.contest.id}/{get_valid_filename(os.path.basename(filename))}" class ContestLogo(models.Model): @@ -24,7 +21,7 @@ class ContestLogo(models.Model): def save(self, *args, **kwargs): self.updated_at = timezone.now() - return super(ContestLogo, self).save(*args, **kwargs) + return super().save(*args, **kwargs) @property def filename(self): @@ -36,10 +33,7 @@ class Meta: def make_icon_filename(instance, filename): - return "icons/%s/%s" % ( - instance.contest.id, - get_valid_filename(os.path.basename(filename)), - ) + return f"icons/{instance.contest.id}/{get_valid_filename(os.path.basename(filename))}" class ContestIcon(models.Model): @@ -49,7 +43,7 @@ class ContestIcon(models.Model): def save(self, *args, **kwargs): self.updated_at = timezone.now() - return super(ContestIcon, self).save(*args, **kwargs) + return super().save(*args, **kwargs) @property def filename(self): diff --git a/oioioi/contestlogo/tests.py b/oioioi/contestlogo/tests.py index 5a494e50d..089a8ab4d 100644 --- a/oioioi/contestlogo/tests.py +++ b/oioioi/contestlogo/tests.py @@ -97,7 +97,7 @@ def test_icon_last_modified(self): icon = ContestIcon.objects.get() icon.image = ContentFile(b"eloziom", name="foo") icon.save() - self.request_file("/c/c/icons/%d/" % icon.pk, icon.updated_at) + self.request_file(f"/c/c/icons/{icon.pk}/", icon.updated_at) def test_logo_last_modified(self): logo = ContestLogo.objects.get() diff --git a/oioioi/contests/admin.py b/oioioi/contests/admin.py index 8d35090bd..fb1f3925f 100644 --- a/oioioi/contests/admin.py +++ b/oioioi/contests/admin.py @@ -65,7 +65,7 @@ class ContestProxyAdminSite(admin.AdminSite): def __init__(self, orig): - super(ContestProxyAdminSite, self).__init__(orig.name) + super().__init__(orig.name) self._orig = orig def register(self, model_or_iterable, admin_class=None, **options): @@ -74,28 +74,28 @@ def register(self, model_or_iterable, admin_class=None, **options): def unregister(self, model_or_iterable): self._orig.unregister(model_or_iterable) try: - super(ContestProxyAdminSite, self).unregister(model_or_iterable) + super().unregister(model_or_iterable) except NotRegistered: pass def contest_register(self, model_or_iterable, admin_class=None, **options): - super(ContestProxyAdminSite, self).register(model_or_iterable, admin_class, **options) + super().register(model_or_iterable, admin_class, **options) def contest_unregister(self, model_or_iterable): - super(ContestProxyAdminSite, self).unregister(model_or_iterable) + super().unregister(model_or_iterable) def get_urls(self): self._registry.update(self._orig._registry) - return super(ContestProxyAdminSite, self).get_urls() + return super().get_urls() def index(self, request, extra_context=None): if request.contest: - return super(ContestProxyAdminSite, self).index(request, extra_context) + return super().index(request, extra_context) return self._orig.index(request, extra_context) def app_index(self, request, app_label, extra_context=None): if request.contest: - return super(ContestProxyAdminSite, self).app_index(request, app_label, extra_context) + return super().app_index(request, app_label, extra_context) return self._orig.app_index(request, app_label, extra_context) @@ -180,7 +180,7 @@ def content_link(self, instance): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "round": kwargs["queryset"] = Round.objects.filter(contest=request.contest) - return super(AttachmentInline, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) class ContestLinkInline(admin.TabularInline): @@ -218,7 +218,7 @@ def get_fields(self, request, obj=None): def get_fieldsets(self, request, obj=None): if obj and not request.GET.get("simple", False): - return super(ContestAdmin, self).get_fieldsets(request, obj) + return super().get_fieldsets(request, obj) fields = list(SimpleContestForm().base_fields.keys()) return [(None, {"fields": fields})] @@ -242,10 +242,10 @@ def get_inlines(self, request, obj): def get_form(self, request, obj=None, **kwargs): if not self.has_change_permission(request, obj): - return super(ContestAdmin, self).get_form(request, obj, **kwargs) + return super().get_form(request, obj, **kwargs) if obj and not request.GET.get("simple", False): - return super(ContestAdmin, self).get_form(request, obj, **kwargs) + return super().get_form(request, obj, **kwargs) return modelform_factory( self.model, form=SimpleContestForm, @@ -255,7 +255,7 @@ def get_form(self, request, obj=None, **kwargs): def get_formsets(self, request, obj=None): if obj and not request.GET.get("simple", False): - return super(ContestAdmin, self).get_formsets(request, obj) + return super().get_formsets(request, obj) return [] def response_change(self, request, obj): @@ -263,10 +263,10 @@ def response_change(self, request, obj): # view. if "_popup" not in request.POST: return HttpResponseRedirect(request.get_full_path()) - return super(ContestAdmin, self).response_change(request, obj) + return super().response_change(request, obj) def response_add(self, request, obj, post_url_continue=None): - default_redirection = super(ContestAdmin, self).response_add(request, obj, post_url_continue) + default_redirection = super().response_add(request, obj, post_url_continue) if "_continue" in request.POST or "_addanother" in request.POST: return default_redirection else: @@ -274,17 +274,17 @@ def response_add(self, request, obj, post_url_continue=None): def response_delete(self, request): set_cc_id(None) - return super(ContestAdmin, self).response_delete(request) + return super().response_delete(request) def _get_extra_context(self, extra_context): extra_context = extra_context or {} - extra_context["categories"] = sorted(set([getattr(inline, "category", None) for inline in self.inlines])) + extra_context["categories"] = sorted({getattr(inline, "category", None) for inline in self.inlines}) extra_context["no_category"] = NO_CATEGORY return extra_context def add_view(self, request, form_url="", extra_context=None): extra_context = self._get_extra_context(extra_context) - ret = super(ContestAdmin, self).add_view(request, form_url, extra_context) + ret = super().add_view(request, form_url, extra_context) create_contest_attributes(request, True) return ret @@ -296,7 +296,7 @@ def change_view(self, request, object_id, form_url="", extra_context=None): create_contest_attributes(request, False) if not request.contest or request.contest.id != contest_id: return redirect("oioioiadmin:contests_contest_change", object_id, contest_id=contest_id) - return super(ContestAdmin, self).change_view(request, object_id, form_url, extra_context) + return super().change_view(request, object_id, form_url, extra_context) def render_change_form(self, request, context, add=False, change=False, form_url="", obj=None): if not add: @@ -315,7 +315,7 @@ def delete_selected_contests(self, modeladmin, request, queryset): def get_actions(self, request): # Use delete_selected with a custom redirect. - actions = super(ContestAdmin, self).get_actions(request) + actions = super().get_actions(request) actions["delete_selected"] = ( self.delete_selected_contests, "delete_selected", @@ -357,7 +357,7 @@ def _attach_problem_ids_to_url(self, queryset, url_name): # Attach problem ids as arguments to the URL base_url = reverse(url_name) query_string = urlencode({"ids": ",".join(str(i) for i in ids)}, doseq=True) - return "%s?%s" % (base_url, query_string) + return f"{base_url}?{query_string}" @action(description=_("Attach problems to another contest")) def attach_problems_to_another_contest(self, request, queryset): @@ -375,7 +375,7 @@ def __init__(self, *args, **kwargs): # creating a thread local variable to store the request self._request_local = threading.local() self._request_local.request = None - super(ProblemInstanceAdmin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def has_add_permission(self, request): return False @@ -393,13 +393,13 @@ def has_delete_permission(self, request, obj=None): def has_view_permission(self, request, obj=None): if is_contest_archived(request): return is_contest_basicadmin(request) - return super(ProblemInstanceAdmin, self).has_view_permission(request, obj) + return super().has_view_permission(request, obj) def _problem_change_href(self, instance): came_from = reverse("oioioiadmin:contests_probleminstance_changelist") came_from_arg = urllib.parse.urlencode({"came_from": came_from}) problem_change_base_href = reverse("oioioiadmin:problems_problem_change", args=(instance.problem_id,)) - return "%s?%s" % (problem_change_base_href, came_from_arg) + return f"{problem_change_base_href}?{came_from_arg}" def _rejudge_all_submissions_for_problem_href(self, instance): return reverse("rejudge_all_submissions_for_problem", args=(instance.id,)) @@ -420,7 +420,7 @@ def _reattach_problem_href(self, instance): base_url = reverse("reattach_problem_contest_list") query_string = urlencode({"ids": instance.id}) # Attach problem id as an argument to the URL - return "%s?%s" % (base_url, query_string) + return f"{base_url}?{query_string}" def _add_or_update_href(self, instance): return reverse("problemset_add_or_update") + "?" + urllib.parse.urlencode({"problem": instance.problem_id, "key": "upload"}) @@ -438,7 +438,7 @@ def _move_href(self, instance): return reverse("oioioiadmin:contests_probleminstance_change", args=(instance.id,)) def get_list_display(self, request): - items = super(ProblemInstanceAdmin, self).get_list_display(request) + items = super().get_list_display(request) if not is_contest_admin(request): disallowed_items = ["package"] items = [item for item in items if item not in disallowed_items] @@ -522,20 +522,20 @@ def package(self, instance): def get_actions(self, request): self._request_local.request = request # Disable delete_selected. - actions = super(ProblemInstanceAdmin, self).get_actions(request) + actions = super().get_actions(request) if "delete_selected" in actions: del actions["delete_selected"] return actions def get_custom_list_select_related(self): - return super(ProblemInstanceAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "contest", "round", "problem", ] def get_queryset(self, request): - qs = super(ProblemInstanceAdmin, self).get_queryset(request) + qs = super().get_queryset(request) qs = ( qs.filter(contest=request.contest) .annotate(localized_name=Subquery(ProblemName.objects.filter(problem=OuterRef("problem__pk"), language=get_language()).values("name"))) @@ -552,7 +552,7 @@ def get_queryset(self, request): def changelist_view(self, request, extra_context=None): extra_context = extra_context or {} extra_context["show_add_button"] = not is_contest_archived(request) - return super(ProblemInstanceAdmin, self).changelist_view(request, extra_context=extra_context) + return super().changelist_view(request, extra_context=extra_context) contest_site.contest_register(ProblemInstance, ProblemInstanceAdmin) @@ -696,7 +696,7 @@ def get_list_filter(self, request): def get_urls(self): urls = [path("rejudge/", self.rejudge_view)] - return urls + super(SubmissionAdmin, self).get_urls() + return urls + super().get_urls() def rejudge_view(self, request): tests = request.POST.getlist("tests", []) @@ -753,7 +753,7 @@ def has_rejudge_permission(self, request): return is_contest_basicadmin(request) def get_actions(self, request): - actions = super(SubmissionAdmin, self).get_actions(request) + actions = super().get_actions(request) if not request.user.is_superuser: if not self.has_delete_permission(request): del actions["delete_selected"] @@ -779,10 +779,7 @@ def user_full_name(self, instance): def problem_instance_display(self, instance): if instance.kind != "NORMAL": - return "%s (%s)" % ( - force_str(instance.problem_instance), - force_str(instance.get_kind_display()), - ) + return f"{force_str(instance.problem_instance)} ({force_str(instance.get_kind_display())})" else: # return instance.problem_instance # FIXME: This is a temporary hack, @@ -852,7 +849,7 @@ def rejudge_action(self, request, queryset): rejudge_action.short_description = _("Rejudge selected submissions") def get_custom_list_select_related(self): - return super(SubmissionAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "user", "problem_instance", "problem_instance__problem", @@ -860,7 +857,7 @@ def get_custom_list_select_related(self): ] def get_queryset(self, request): - queryset = super(SubmissionAdmin, self).get_queryset(request) + queryset = super().get_queryset(request) if request.contest: queryset = queryset.filter(problem_instance__contest=request.contest) queryset = queryset.order_by("-id") @@ -873,7 +870,7 @@ def get_queryset(self, request): def lookup_allowed(self, key, value, request): if key == "user__username": return True - return super(SubmissionAdmin, self).lookup_allowed(key, value, request) + return super().lookup_allowed(key, value, request) def change_view(self, request, object_id, form_url="", extra_context=None): _contest_id = None @@ -977,16 +974,16 @@ def user_full_name(self, instance): user_full_name.admin_order_field = "user__last_name" def get_queryset(self, request): - qs = super(RoundTimeExtensionAdmin, self).get_queryset(request) + qs = super().get_queryset(request) return qs.filter(round__contest=request.contest) def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "round": kwargs["queryset"] = Round.objects.filter(contest=request.contest) - return super(RoundTimeExtensionAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) def get_custom_list_select_related(self): - return super(RoundTimeExtensionAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "user", "round__contest", ] @@ -1051,7 +1048,7 @@ def has_delete_permission(self, request, obj=None): return self.has_change_permission(request, obj) def get_queryset(self, request): - qs = super(ContestPermissionAdmin, self).get_queryset(request) + qs = super().get_queryset(request) if request.contest: qs = qs.filter(contest=request.contest) return qs @@ -1063,14 +1060,14 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): qs = qs.filter(id=request.contest.id) kwargs["initial"] = request.contest kwargs["queryset"] = qs - return super(ContestPermissionAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) def formfield_for_choice_field(self, db_field, request, **kwargs): if db_field.name == "permission": # Contest owners musn't manage other contest owners if not request.user.is_superuser: kwargs["choices"] = [i for i in contest_permissions if i[0] != "contests.contest_owner"] - return super(ContestPermissionAdmin, self).formfield_for_choice_field(db_field, request, **kwargs) + return super().formfield_for_choice_field(db_field, request, **kwargs) contest_site.register(ContestPermission, ContestPermissionAdmin) diff --git a/oioioi/contests/attachment_registration.py b/oioioi/contests/attachment_registration.py index 3f0457672..5b90bcf5e 100644 --- a/oioioi/contests/attachment_registration.py +++ b/oioioi/contests/attachment_registration.py @@ -24,7 +24,7 @@ def register(self, attachment_generator=None, order=sys.maxsize): def to_list(self, **kwargs): attachments = [] - for idx, gen in enumerate(self._registry): + for _idx, gen in enumerate(self._registry): attachments.extend(gen(**kwargs)) return attachments diff --git a/oioioi/contests/controllers.py b/oioioi/contests/controllers.py index f6c3bf4e0..20774b23c 100644 --- a/oioioi/contests/controllers.py +++ b/oioioi/contests/controllers.py @@ -1039,7 +1039,7 @@ def can_see_round(self, request_or_context, round, no_admin=False): if preparation_start < context.timestamp < preparation_end: return False - return super(PastRoundsHiddenContestControllerMixin, self).can_see_round(request_or_context, round, no_admin) + return super().can_see_round(request_or_context, round, no_admin) class NotificationsMixinForContestController: diff --git a/oioioi/contests/date_registration.py b/oioioi/contests/date_registration.py index eae744e96..e1881dc08 100644 --- a/oioioi/contests/date_registration.py +++ b/oioioi/contests/date_registration.py @@ -57,13 +57,19 @@ def decorator(original_class): return decorator if name_generator is None: - name_generator = lambda obj: str(model._meta.verbose_name) + " " + str(model._meta.get_field(date_field).verbose_name) + + def name_generator(obj): + return str(model._meta.verbose_name) + " " + str(model._meta.get_field(date_field).verbose_name) if round_chooser is None: - round_chooser = lambda obj: None + + def round_chooser(obj): + return None if qs_filter is None: - qs_filter = lambda qs, contest_id: qs.filter(contest=contest_id) + + def qs_filter(qs, contest_id): + return qs.filter(contest=contest_id) date_item = self.DateItem(date_field, name_generator, round_chooser, qs_filter, model) self._registry.register(date_item, order) @@ -76,15 +82,15 @@ def tolist(self, contest_id): instances = item.qs_filter(model.objects.all(), contest_id) for instance in instances: context_items.append( - dict( - text=item.name_generator(instance), - date=getattr(instance, item.date_field), - date_field=item.date_field, - model=model, - id=instance.id, - round=item.round_chooser(instance), - order=self._registry.keys[idx], - ) + { + "text": item.name_generator(instance), + "date": getattr(instance, item.date_field), + "date_field": item.date_field, + "model": model, + "id": instance.id, + "round": item.round_chooser(instance), + "order": self._registry.keys[idx], + } ) return context_items diff --git a/oioioi/contests/fields.py b/oioioi/contests/fields.py index fd884f497..cf0b246cd 100644 --- a/oioioi/contests/fields.py +++ b/oioioi/contests/fields.py @@ -12,7 +12,7 @@ class ScoreField(models.CharField): def __init__(self, *args, **kwargs): kwargs.setdefault("max_length", 255) - super(ScoreField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def get_prep_value(self, value): if value is None: diff --git a/oioioi/contests/forms.py b/oioioi/contests/forms.py index dbff38dbd..193dd03fd 100644 --- a/oioioi/contests/forms.py +++ b/oioioi/contests/forms.py @@ -68,7 +68,7 @@ def _set_dates(self, round): setattr(round, date, self.cleaned_data.get(date)) def __init__(self, *args, **kwargs): - super(SimpleContestForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) instance = kwargs.get("instance", None) if instance is not None: rounds = instance.round_set.all() @@ -85,14 +85,14 @@ def __init__(self, *args, **kwargs): self._generate_default_dates() def clean(self): - cleaned_data = super(SimpleContestForm, self).clean() + cleaned_data = super().clean() round = Round() self._set_dates(round) round.clean() return cleaned_data def save(self, commit=True): - instance = super(SimpleContestForm, self).save(commit=False) + instance = super().save(commit=False) rounds = instance.round_set.all() if len(rounds) > 1: raise ValueError("SimpleContestForm does not support contests with more than one round.") @@ -113,7 +113,7 @@ def save(self, commit=True): class ProblemInstanceForm(forms.ModelForm): def __init__(self, *args, **kwargs): instance = kwargs.get("instance") - super(ProblemInstanceForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if instance and not instance.contest.is_archived: self.fields["round"].queryset = instance.contest.round_set self.fields["round"].required = True @@ -174,7 +174,7 @@ def __init__(self, request, *args, **kwargs): pi_choices = [(pi.id, str(pi)) for pi in pis] # init form with previously sent data - super(SubmissionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # prepare problem instance selector pi_field = self.fields["problem_instance_id"] @@ -274,7 +274,7 @@ def is_valid(self): return forms.Form.is_valid(self) def clean(self, check_submission_limit=True, check_round_times=True): - cleaned_data = super(SubmissionForm, self).clean() + cleaned_data = super().clean() if "kind" not in cleaned_data: cleaned_data["kind"] = self.kind @@ -305,7 +305,7 @@ class SubmissionFormForProblemInstance(SubmissionForm): def __init__(self, request, problem_instance, *args, **kwargs): self.problem_instance = problem_instance kwargs["problem_instance"] = problem_instance - super(SubmissionFormForProblemInstance, self).__init__(request, *args, **kwargs) + super().__init__(request, *args, **kwargs) pis = self.fields["problem_instance_id"] pis.widget.attrs["readonly"] = "True" pis.widget.attrs["data-submit"] = "hidden" @@ -318,13 +318,13 @@ class GetUserInfoForm(forms.Form): user = UserSelectionField(label=_("Username")) def __init__(self, request, *args, **kwargs): - super(GetUserInfoForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["user"].hints_url = reverse("contest_user_hints", kwargs={"contest_id": request.contest.id}) class TestsSelectionForm(forms.Form): def __init__(self, request, queryset, pis_count, uses_is_active, *args, **kwargs): - super(TestsSelectionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) problem_instance = queryset[0].problem_instance tests = Test.objects.filter(problem_instance=problem_instance, is_active=True) @@ -394,7 +394,7 @@ class RoundSelectionForm(forms.Form): def __init__(self, *args, **kwargs): contest = kwargs.pop("contest", None) - super(RoundSelectionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if contest is None: raise ValueError("Contest must be provided to RoundSelectionForm.") self.fields["round"].queryset = Round.objects.filter(contest=contest) diff --git a/oioioi/contests/handlers.py b/oioioi/contests/handlers.py index ed4e1dd21..cfcf10428 100644 --- a/oioioi/contests/handlers.py +++ b/oioioi/contests/handlers.py @@ -210,7 +210,7 @@ def mail_admins_on_error(env, submission, exc_info, **kwargs): try: mail_admins( - "System Error evaluating submission #%s" % env.get("submission_id", "???"), + "System Error evaluating submission #{}".format(env.get("submission_id", "???")), "".join(traceback.format_exception(*exc_info)), ) except (OSError, SMTPException) as e: diff --git a/oioioi/contests/management/commands/hidden_results_diff.py b/oioioi/contests/management/commands/hidden_results_diff.py index 0b7104ac8..b9fd0ec71 100644 --- a/oioioi/contests/management/commands/hidden_results_diff.py +++ b/oioioi/contests/management/commands/hidden_results_diff.py @@ -61,4 +61,4 @@ def handle(self, *args, **options): new_score = new_report.score_report.score if old_score != new_score: - print("%s: %s -> %s" % (s, old_score, new_score)) + print(f"{s}: {old_score} -> {new_score}") diff --git a/oioioi/contests/models.py b/oioioi/contests/models.py index d3b8d9ebf..579acba69 100644 --- a/oioioi/contests/models.py +++ b/oioioi/contests/models.py @@ -28,12 +28,9 @@ def make_contest_filename(instance, filename): if not isinstance(instance, Contest): - assert hasattr(instance, "contest"), "contest_file_generator used on object %r which does not have 'contest' attribute" % (instance,) + assert hasattr(instance, "contest"), f"contest_file_generator used on object {instance!r} which does not have 'contest' attribute" instance = instance.contest - return "contests/%s/%s" % ( - instance.id, - get_valid_filename(os.path.basename(filename)), - ) + return f"contests/{instance.id}/{get_valid_filename(os.path.basename(filename))}" class Contest(models.Model): @@ -92,7 +89,7 @@ class Contest(models.Model): def save(self, *args, **kwargs): if not self.controller_name: self.controller_name = "oioioi.teachers.controllers.TeacherContestController" - super(Contest, self).save(*args, **kwargs) + super().save(*args, **kwargs) @property def controller(self): @@ -505,13 +502,8 @@ def get_score_display(self): return self.problem_instance.controller.render_submission_score(self) def __str__(self): - return "Submission(%d, %s, %s, %s, %s, %s)" % ( - self.id, - self.problem_instance.problem.name, - self.user.username if self.user else None, - self.date, - self.kind, - self.status, + return ( + f"Submission({self.id}, {self.problem_instance.problem.name}, {self.user.username if self.user else None}, {self.date}, {self.kind}, {self.status})" ) @@ -657,7 +649,7 @@ class Meta: verbose_name_plural = _("contest permissions") def __str__(self): - return "%s/%s: %s" % (self.contest, self.permission, self.user) + return f"{self.contest}/{self.permission}: {self.user}" class ContestView(models.Model): @@ -672,7 +664,7 @@ class Meta: ordering = ("-timestamp",) def __str__(self): - return "%s,%s" % (self.user, self.contest) + return f"{self.user},{self.contest}" class ContestLink(models.Model): @@ -694,9 +686,11 @@ def contest_links_generator(request): for link in links: # pylint: disable=cell-var-from-loop # http://docs.python-guide.org/en/latest/writing/gotchas/#late-binding-closures - url_generator = lambda request, url=link.url: url + def url_generator(request, url=link.url): + return url + item = MenuItem( - name="contest_link_%d" % link.id, + name=f"contest_link_{link.id}", text=link.description, url_generator=url_generator, order=link.order, diff --git a/oioioi/contests/scores.py b/oioioi/contests/scores.py index 55835df90..92bdfd36b 100644 --- a/oioioi/contests/scores.py +++ b/oioioi/contests/scores.py @@ -35,7 +35,7 @@ class ScoreValue(ClassInitBase): #: representation of the value. This must be overridden in all subclasses. symbol = "__override_in_subclasses__" - _subclasses = dict() + _subclasses = {} @classmethod def __classinit__(cls): @@ -49,14 +49,14 @@ def __classinit__(cls): return if cls.symbol == this_class.symbol: - raise AssertionError("Symbol attribute not defined in %r" % (cls,)) + raise AssertionError(f"Symbol attribute not defined in {cls!r}") if cls.symbol in this_class._subclasses: - raise AssertionError("Duplicate symbol '%s' used in both %r and %r" % (cls.symbol, this_class._subclasses[cls.symbol], cls)) + raise AssertionError(f"Duplicate symbol '{cls.symbol}' used in both {this_class._subclasses[cls.symbol]!r} and {cls!r}") this_class._subclasses[cls.symbol] = cls def serialize(self): """Converts the instance of any subclass to string.""" - return "%s:%s" % (self.symbol, self._to_repr()) + return f"{self.symbol}:{self._to_repr()}" def __repr__(self): return self.serialize() @@ -68,7 +68,7 @@ def deserialize(serialized): return None parts = serialized.split(":", 1) if len(parts) < 2: - raise ValidationError(_("Score must look like this: ':', for example 'int:100', not '%s'." % (serialized,))) + raise ValidationError(_("Score must look like this: ':', for example 'int:100', not '{}'.".format(serialized))) symbol, value = parts if symbol in ScoreValue._subclasses: return ScoreValue._subclasses[symbol]._from_repr(value) @@ -164,14 +164,14 @@ def __unicode__(self): return str(self.value) def __repr__(self): - return "IntegerScore(%s)" % (self.value,) + return f"IntegerScore({self.value})" @classmethod def _from_repr(cls, value): return cls(int(value)) def _to_repr(self): - return "%019d" % self.value + return f"{self.value:019d}" def to_int(self): return self.value diff --git a/oioioi/contests/serializers.py b/oioioi/contests/serializers.py index 2a017c54d..eb42e5886 100644 --- a/oioioi/contests/serializers.py +++ b/oioioi/contests/serializers.py @@ -16,7 +16,7 @@ def __init__(self, pi, *args, **kwargs): if pi is not None: self.problem_instance_id = serializers.HiddenField(default=pi.pk) self.problem_instance = pi - super(SubmissionSerializer, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def validate(self, data): for field in SubmissionSerializer.Meta.fields: diff --git a/oioioi/contests/templatetags/get_user_name.py b/oioioi/contests/templatetags/get_user_name.py index b01c6f1c0..26b8040d0 100644 --- a/oioioi/contests/templatetags/get_user_name.py +++ b/oioioi/contests/templatetags/get_user_name.py @@ -50,7 +50,7 @@ def _get_user_name(self, context, user): def _get_name(parser, token, tag_name): bits = token.split_contents() if (len(bits) != 4 or bits[2] != "as") and (len(bits) != 2): - raise TemplateSyntaxError("The tag should look like this: {%% %s [ as ] %%}" % tag_name) + raise TemplateSyntaxError(f"The tag should look like this: {{% {tag_name} [ as ] %}}") asvar = None if len(bits) == 4: asvar = bits[3] diff --git a/oioioi/contests/tests/__init__.py b/oioioi/contests/tests/__init__.py index 3701eb0b2..990c4a75a 100644 --- a/oioioi/contests/tests/__init__.py +++ b/oioioi/contests/tests/__init__.py @@ -1,5 +1,3 @@ -from django.core.files.base import ContentFile -from django.db.models import Q from django.urls import reverse from oioioi.base.utils.query_helpers import Q_always_false @@ -81,7 +79,7 @@ def make_empty_contest_formset(): ("contestcompiler_set", 0, 0, 0, 1000), ("checkerformatforcontest", 0, 0, 0, 1), ) - data = dict() + data = {} for name, total, initial, min_num, max_num in formsets: data[f"{name}-TOTAL_FORMS"] = total data[f"{name}-INITIAL_FORMS"] = initial diff --git a/oioioi/contests/tests/tests.py b/oioioi/contests/tests/tests.py index 0655475c1..c0f6836bb 100755 --- a/oioioi/contests/tests/tests.py +++ b/oioioi/contests/tests/tests.py @@ -197,8 +197,8 @@ def test_default_order(self): def check_id_order_in_response(self, response, ids): self.check_order_in_response( response, - ["/submission/%d/change" % x for x in ids], - "Submission with id %d should be displayed before submission with id %d" % tuple(ids), + [f"/submission/{x}/change" for x in ids], + f"Submission with id {ids[0]} should be displayed before submission with id {ids[1]}", ) @pytest.mark.skip(reason="TODO: Repair the ordering platform-wide.") @@ -240,12 +240,12 @@ def check_order_in_response(self, response, order, error_msg): self.assertIn( test_first, table_content, - "Fixtures should contain submission with %s" % test_first, + f"Fixtures should contain submission with {test_first}", ) self.assertIn( test_second, table_content, - "Fixtures should contain submission with %s" % test_second, + f"Fixtures should contain submission with {test_second}", ) test_first_index = table_content.index(test_first) @@ -576,8 +576,8 @@ def test_recent_contests_list(self): invisible_contest.save() self.assertTrue(self.client.login(username="test_admin")) - self.client.get("/c/%s/dashboard/" % contest.id) - self.client.get("/c/%s/dashboard/" % invisible_contest.id) + self.client.get(f"/c/{contest.id}/dashboard/") + self.client.get(f"/c/{invisible_contest.id}/dashboard/") response = self.client.get(reverse("select_contest")) self.assertEqual(len(response.context["contests"]), 2) self.assertContains(response, "Test contest") @@ -585,15 +585,15 @@ def test_recent_contests_list(self): self.client.logout() self.assertTrue(self.client.login(username="test_admin")) - response = self.client.get("/c/%s/dashboard/" % contest.id) + response = self.client.get(f"/c/{contest.id}/dashboard/") self.assertContains(response, "dropdown open") - response = self.client.get("/c/%s/dashboard/" % contest.id) + response = self.client.get(f"/c/{contest.id}/dashboard/") self.assertNotContains(response, "dropdown open") contests = [cv.contest for cv in ContestView.objects.all()] self.assertEqual(contests, [contest, invisible_contest]) - self.client.get("/c/%s/dashboard/" % invisible_contest.id) + self.client.get(f"/c/{invisible_contest.id}/dashboard/") response = self.client.get(reverse("select_contest")) self.assertEqual(len(response.context["contests"]), 2) contests = [cv.contest for cv in ContestView.objects.all()] @@ -1176,7 +1176,7 @@ def failing_handler(env): class BrokenContestController(ProgrammingContestController): def fill_evaluation_environ(self, environ, submission): - super(BrokenContestController, self).fill_evaluation_environ(environ, submission) + super().fill_evaluation_environ(environ, submission) environ.setdefault("recipe", []).append(("failing_handler", "oioioi.contests.tests.tests.failing_handler")) @@ -1642,17 +1642,17 @@ def check(visible, invisible): # File list response = self.client.get(list_url) self.assertEqual(response.status_code, 200) - for att, content, name in visible: + for att, _content, name in visible: self.assertContains(response, name) self.assertContains(response, att.description) - for att, content, name in invisible: + for att, _content, name in invisible: self.assertNotContains(response, name) self.assertNotContains(response, att.description) for f in response.context["files"]: self.assertEqual(f["admin_only"], False) # Actual accessibility - for att, content, name in visible: + for att, content, _name in visible: response = self.client.get( reverse( get_attachment_urlpattern_name(att), @@ -1660,7 +1660,7 @@ def check(visible, invisible): ) ) self.assertStreamingEqual(response, content) - for att, content, name in invisible: + for att, _content, _name in invisible: check_not_accessible( self, get_attachment_urlpattern_name(att), @@ -1671,15 +1671,15 @@ def check(visible, invisible): self.assertTrue(self.client.login(username="test_admin")) response = self.client.get(list_url) self.assertEqual(response.status_code, 200) - for att, content, name in visible + invisible: + for att, _content, name in visible + invisible: self.assertContains(response, name) self.assertContains(response, att.description) - invisible_names = set([f[2] for f in invisible]) + invisible_names = {f[2] for f in invisible} for f in response.context["files"]: self.assertEqual(f["admin_only"], f["name"] in invisible_names) # Actual accessibility as an admin - for att, content, name in visible + invisible: + for att, content, _name in visible + invisible: response = self.client.get( reverse( get_attachment_urlpattern_name(att), @@ -3819,7 +3819,7 @@ def see_link_for_submission_on_problem_list(self, username, should_see): contest = Contest.objects.get(pk="c") problems_url = reverse("problems_list", kwargs={"contest_id": contest.id}) submission_url = reverse("submission", kwargs={"contest_id": contest.id, "submission_id": 1}) - expected_hyperlink = '' % submission_url + expected_hyperlink = f'' response = self.client.get(problems_url, follow=True) @@ -3903,7 +3903,7 @@ class TestAPISubmitBase(APITestCase): def __init__(self, *args, **kwargs): self.fixtures += self.extra_fixtures - super(TestAPISubmitBase, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def setUp(self): self.client.force_authenticate(user=User.objects.get(username="test_user")) @@ -3988,7 +3988,7 @@ def test_size_limit_accuracy(self): self._assertSubmitted(response, 2) def _assertUnsupportedExtension(self, contest, problem_instance, name, ext): - response = self.contest_submit(contest, problem_instance, file_name="%s.%s" % (name, ext)) + response = self.contest_submit(contest, problem_instance, file_name=f"{name}.{ext}") self.assertContains(response, "Unknown or not supported file extension.", status_code=400) def test_limiting_extensions(self): diff --git a/oioioi/contests/utils.py b/oioioi/contests/utils.py index cf139982b..dd3954128 100755 --- a/oioioi/contests/utils.py +++ b/oioioi/contests/utils.py @@ -138,22 +138,19 @@ def generic_rounds_times(request=None, contest=None): if not request or not hasattr(request, "user") or request.user.is_anonymous: rtexts = {} else: - rtexts = dict((x["round_id"], x) for x in RoundTimeExtension.objects.filter(user=request.user, round__id__in=rids).values()) - - result = dict( - ( - r, - RoundTimes( - r.start_date, - r.end_date, - r.contest, - r.results_date, - r.public_results_date, - rtexts[r.id]["extra_time"] if r.id in rtexts else 0, - ), + rtexts = {x["round_id"]: x for x in RoundTimeExtension.objects.filter(user=request.user, round__id__in=rids).values()} + + result = { + r: RoundTimes( + r.start_date, + r.end_date, + r.contest, + r.results_date, + r.public_results_date, + rtexts[r.id]["extra_time"] if r.id in rtexts else 0, ) for r in rounds - ) + } if request is not None: getattr(request, cache_attribute)[contest.id] = result return result @@ -341,7 +338,7 @@ def get_results_visibility(request): """Returns the results ad ranking visibility for each round in the contest""" rtimes = rounds_times(request, request.contest) - dates = list() + dates = [] for r in rtimes.keys(): results_date = rtimes[r].results_date() public_results_date = rtimes[r].public_results_date() @@ -581,7 +578,7 @@ def best_round_to_display(request, allow_past_rounds=False): past_rtimes = None if timestamp and contest: - rtimes = dict((round, contest.controller.get_round_times(request, round)) for round in Round.objects.filter(contest=contest)) + rtimes = {round: contest.controller.get_round_times(request, round) for round in Round.objects.filter(contest=contest)} next_rtimes = [(r, rt) for r, rt in rtimes.items() if rt.is_future(timestamp)] next_rtimes.sort(key=lambda r_rt: r_rt[1].get_start()) current_rtimes = [(r, rt) for r, rt in rtimes if rt.is_active(timestamp) and rt.get_end()] diff --git a/oioioi/contests/views.py b/oioioi/contests/views.py index 8d6f3f863..eca82f7c2 100755 --- a/oioioi/contests/views.py +++ b/oioioi/contests/views.py @@ -182,8 +182,8 @@ def problems_list_view(request): key=lambda p: (p[2].get_key_for_comparison(), p[0].round.name, p[0].short_name), ) - show_submissions_limit = any([p[6] for p in problems_statements]) - show_submit_button = any([p[7] for p in problems_statements]) + show_submissions_limit = any(p[6] for p in problems_statements) + show_submit_button = any(p[7] for p in problems_statements) show_rounds = len(frozenset(pi.round_id for pi in problem_instances)) > 1 table_columns = 3 + int(show_problems_limits) + int(show_submissions_limit) + int(show_submit_button) diff --git a/oioioi/dashboard/controllers.py b/oioioi/dashboard/controllers.py index 4158b82ec..e7a38c144 100644 --- a/oioioi/dashboard/controllers.py +++ b/oioioi/dashboard/controllers.py @@ -13,7 +13,7 @@ def default_view(self, request): if request.contest and can_enter_contest(request): return reverse("contest_dashboard", kwargs={"contest_id": self.contest.id}) else: - return super(DashboardDefaultViewMixin, self).default_view(request) + return super().default_view(request) ContestController.mix_in(DashboardDefaultViewMixin) diff --git a/oioioi/deployment/create_config.py b/oioioi/deployment/create_config.py index f3606b8f0..638b76d5f 100644 --- a/oioioi/deployment/create_config.py +++ b/oioioi/deployment/create_config.py @@ -134,7 +134,7 @@ def main(): absolute_dir = os.path.abspath(args.dir) if os.path.exists(absolute_dir): - error("%s already exists; please specify another location" % (absolute_dir,)) + error(f"{absolute_dir} already exists; please specify another location") os.makedirs(absolute_dir) diff --git a/oioioi/disqualification/admin.py b/oioioi/disqualification/admin.py index 86d5ba4c0..9fa01c203 100644 --- a/oioioi/disqualification/admin.py +++ b/oioioi/disqualification/admin.py @@ -49,7 +49,7 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): if request.contest: qs = request.contest.controller.registration_controller().filter_participants(qs) kwargs["queryset"] = qs - return super(DisqualificationAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) def submission_link(self, instance): if instance.submission is None: @@ -60,11 +60,7 @@ def submission_link(self, instance): } return make_html_link( reverse("submission", kwargs=reverse_kwargs), - "%d (%s)" - % ( - instance.submission_id, - force_str(instance.submission.problem_instance), - ), + f"{instance.submission_id} ({force_str(instance.submission.problem_instance)})", ) submission_link.short_description = _("Submission") @@ -82,14 +78,14 @@ def guilty_text(self, instance): guilty_text.admin_order_field = "guilty" def get_custom_list_select_related(self): - return super(DisqualificationAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "submission", "user", "submission__problem_instance", ] def get_queryset(self, request): - return super(DisqualificationAdmin, self).get_queryset(request).filter(contest=request.contest).order_by("-id") + return super().get_queryset(request).filter(contest=request.contest).order_by("-id") contest_site.contest_register(Disqualification, DisqualificationAdmin) @@ -124,7 +120,7 @@ class DisqualificationsAdminMixin: to an admin panel.""" def __init__(self, *args, **kwargs): - super(DisqualificationsAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (DisqualificationsConfigInline,) diff --git a/oioioi/disqualification/controllers.py b/oioioi/disqualification/controllers.py index fd291d24e..4de5fceba 100644 --- a/oioioi/disqualification/controllers.py +++ b/oioioi/disqualification/controllers.py @@ -66,7 +66,7 @@ def exclude_disqualified_users(self, queryset): def change_submission_kind(self, submission, kind): """Changing the kind of submission should undisqualify given submission""" old_kind = submission.kind - super(DisqualificationContestControllerMixin, self).change_submission_kind(submission, kind) + super().change_submission_kind(submission, kind) if submission.kind != old_kind: Disqualification.objects.filter(submission=submission).update(guilty=False) @@ -134,7 +134,7 @@ def _render_contestwide_disqualification_reason(self, request, user): ) def render_my_submissions_header(self, request, submissions): - header = super(DisqualificationContestControllerMixin, self).render_my_submissions_header(request, submissions) + header = super().render_my_submissions_header(request, submissions) disq_header = self.render_disqualifications(request, request.user, submissions) if disq_header: header += disq_header @@ -181,7 +181,7 @@ def render_disqualifications(self, request, user, submissions): def get_contest_participant_info_list(self, request, user): submissions = Submission.objects.filter(problem_instance__contest=request.contest, user=user).order_by("-date").select_related() - info_list = super(DisqualificationContestControllerMixin, self).get_contest_participant_info_list(request, user) + info_list = super().get_contest_participant_info_list(request, user) disqualification = self.render_disqualifications(request, user, submissions) if disqualification: @@ -196,7 +196,7 @@ class DisqualificationProgrammingContestControllerMixin: """ContestController mixin that renders submission disqualification info.""" def render_submission(self, request, submission): - prev = super(DisqualificationProgrammingContestControllerMixin, self).render_submission(request, submission) + prev = super().render_submission(request, submission) if self.is_submission_disqualified(submission) or (is_contest_admin(request) and self.has_disqualification_history(submission)): return prev + self.render_submission_disqualifiaction(request, submission) @@ -220,7 +220,7 @@ def _show_disqualified(self, key): return self.is_admin_key(key) def filter_users_for_ranking(self, key, queryset): - qs = super(WithDisqualificationRankingControllerMixin, self).filter_users_for_ranking(key, queryset) + qs = super().filter_users_for_ranking(key, queryset) if not self._show_disqualified(key): qs = self.contest.controller.exclude_disqualified_users(qs) @@ -229,26 +229,26 @@ def filter_users_for_ranking(self, key, queryset): def _render_ranking_page(self, key, data, page): if not self._show_disqualified(key): - return super(WithDisqualificationRankingControllerMixin, self)._render_ranking_page(key, data, page) + return super()._render_ranking_page(key, data, page) request = self._fake_request(page) data["is_admin"] = self.is_admin_key(key) return render_to_string("disqualification/default-ranking.html", context=data, request=request) def _get_csv_header(self, key, data): - header = super(WithDisqualificationRankingControllerMixin, self)._get_csv_header(key, data) + header = super()._get_csv_header(key, data) if self._show_disqualified(key): header.append(_("Disqualified")) return header def _get_csv_row(self, key, row): - line = super(WithDisqualificationRankingControllerMixin, self)._get_csv_row(key, row) + line = super()._get_csv_row(key, row) if self._show_disqualified(key): line.append(_("Yes") if row.get("disqualified") else _("No")) return line def serialize_ranking(self, key): - data = super(WithDisqualificationRankingControllerMixin, self).serialize_ranking(key) + data = super().serialize_ranking(key) if not self._show_disqualified(key): return data return self._annotate_disqualified(key, data) @@ -262,7 +262,7 @@ def _annotate_disqualified(self, key, data): return data def _ignore_in_ranking_places(self, data_row): - prev = super(WithDisqualificationRankingControllerMixin, self)._ignore_in_ranking_places(data_row) + prev = super()._ignore_in_ranking_places(data_row) return prev or data_row.get("disqualified", False) diff --git a/oioioi/disqualification/models.py b/oioioi/disqualification/models.py index ed538b830..54b43dc39 100644 --- a/oioioi/disqualification/models.py +++ b/oioioi/disqualification/models.py @@ -40,7 +40,7 @@ def save(self, *args, **kwargs): assert self.contest.id == self.submission.problem_instance.contest_id assert self.user.id == self.submission.user_id - super(Disqualification, self).save(*args, **kwargs) + super().save(*args, **kwargs) class DisqualificationsConfig(models.Model): diff --git a/oioioi/disqualification/tests.py b/oioioi/disqualification/tests.py index 56dcef8d3..ee4c43e3d 100644 --- a/oioioi/disqualification/tests.py +++ b/oioioi/disqualification/tests.py @@ -142,19 +142,28 @@ def _assert_submission(self, submission_id, disqualified): def test_dashboard(self): self.assertTrue(self.client.login(username="test_user")) - response_cb = lambda: self.client.get(reverse("contest_dashboard", kwargs=self.contest_kwargs), follow=True) + + def response_cb(): + return self.client.get(reverse("contest_dashboard", kwargs=self.contest_kwargs), follow=True) + self._assert_disqualification_box(response_cb) def test_my_submissions(self): self.assertTrue(self.client.login(username="test_user")) - response_cb = lambda: self.client.get(reverse("my_submissions", kwargs=self.contest_kwargs)) + + def response_cb(): + return self.client.get(reverse("my_submissions", kwargs=self.contest_kwargs)) + self._assert_disqualification_box(response_cb) def test_user_info_page(self): self.assertTrue(self.client.login(username="test_admin")) user = User.objects.get(username="test_user") contest = Contest.objects.get() - response_callback = lambda: self.client.get(reverse("user_info", kwargs={"contest_id": contest.id, "user_id": user.id})) + + def response_callback(): + return self.client.get(reverse("user_info", kwargs={"contest_id": contest.id, "user_id": user.id})) + self._assert_disqualification_box(response_callback) diff --git a/oioioi/evalmgr/admin.py b/oioioi/evalmgr/admin.py index 7ac123720..4b01a6161 100644 --- a/oioioi/evalmgr/admin.py +++ b/oioioi/evalmgr/admin.py @@ -97,7 +97,7 @@ class SystemJobsQueueAdmin(admin.ModelAdmin): actions = ["remove_from_queue", "delete_selected"] def __init__(self, *args, **kwargs): - super(SystemJobsQueueAdmin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.list_display_links = None def _get_link(self, caption, app, *args, **kwargs): @@ -173,14 +173,14 @@ def remove_from_queue(self, request, queryset): remove_from_queue.short_description = _("Remove selected submissions from the queue") def get_queryset(self, request): - qs = super(SystemJobsQueueAdmin, self).get_queryset(request) + qs = super().get_queryset(request) return qs.exclude(state="CANCELLED") def has_delete_permission(self, request, obj=None): return is_contest_admin(request) def get_custom_list_select_related(self): - return super(SystemJobsQueueAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "submission__problem_instance", "submission__problem_instance__contest", "submission__problem_instance__problem", @@ -205,7 +205,7 @@ class Meta: class ContestJobsQueueAdmin(SystemJobsQueueAdmin): def __init__(self, *args, **kwargs): - super(ContestJobsQueueAdmin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.list_display = [x for x in self.list_display if x not in ("contest", "celery_task_id_link")] self.list_display_links = None self.list_filter = self.list_filter + [UserListFilter] @@ -216,7 +216,7 @@ def has_change_permission(self, request, obj=None): return is_contest_admin(request) def get_queryset(self, request): - qs = super(ContestJobsQueueAdmin, self).get_queryset(request) + qs = super().get_queryset(request) return qs.filter(submission__problem_instance__contest=request.contest) diff --git a/oioioi/evalmgr/tasks.py b/oioioi/evalmgr/tasks.py index a43c7b28c..887080322 100644 --- a/oioioi/evalmgr/tasks.py +++ b/oioioi/evalmgr/tasks.py @@ -27,14 +27,14 @@ def _find_placeholder(recipe, name): for i, entry in enumerate(recipe): if entry[0] == name and entry[1] == placeholder: return i - raise IndexError("Placeholder '%s' not found in recipe" % (name,)) + raise IndexError(f"Placeholder '{name}' not found in recipe") def find_recipe_entry(recipe, name): for i, entry in enumerate(recipe): if entry[0] == name: return i - raise IndexError("Entry '%s' not found in recipe" % (name,)) + raise IndexError(f"Entry '{name}' not found in recipe") def recipe_placeholder(name): @@ -143,7 +143,7 @@ def _run_phase(env, phase, extra_kwargs=None): phaseName = phase[0] handlerName = phase[1] if len(phase) not in [2, 3]: - raise TypeError("Receipt element has length neither 2 nor 3: %r" % phase) + raise TypeError(f"Receipt element has length neither 2 nor 3: {phase!r}") if len(phase) == 2: kwargs = {} if len(phase) == 3: @@ -153,7 +153,7 @@ def _run_phase(env, phase, extra_kwargs=None): handler_func = import_string(handlerName) env = handler_func(env, **kwargs) if env is None: - raise RuntimeError('Evaluation handler "%s" (%s) forgot to return the environment.' % (phaseName, handlerName)) + raise RuntimeError(f'Evaluation handler "{phaseName}" ({handlerName}) forgot to return the environment.') return env @@ -218,7 +218,7 @@ def _run_error_handlers(env, exc_info): error_handlers = env.get("error_handlers", []) try: for phase in error_handlers: - env = _run_phase(env, phase, extra_kwargs=dict(exc_info=exc_info)) + env = _run_phase(env, phase, extra_kwargs={"exc_info": exc_info}) # pylint: disable=broad-except except Exception: logger.error( @@ -321,7 +321,7 @@ def prepare_transfer_handler(environ, **kwargs): if "recipe" not in env: raise RuntimeError('No recipe found in job environment. Did you forget to set environ["run_externally"]?') if "error" in env: - raise RuntimeError("Error from workers:\n%s\nTB:\n%s" % (env["error"]["message"], env["error"]["traceback"])) + raise RuntimeError("Error from workers:\n{}\nTB:\n{}".format(env["error"]["message"], env["error"]["traceback"])) _mark_job_state(env, "PROGRESS") while True: recipe = env.get("recipe") diff --git a/oioioi/evalmgr/tests/tests.py b/oioioi/evalmgr/tests/tests.py index e6b7d19fb..d4daf8485 100644 --- a/oioioi/evalmgr/tests/tests.py +++ b/oioioi/evalmgr/tests/tests.py @@ -58,20 +58,20 @@ def rest_handler(env, **kwargs): class TestLocalJobs(TestCase): def test_evalmgr_job(self): env = create_environ() - env.update(dict(recipe=hunting, area="forest")) + env.update({"recipe": hunting, "area": "forest"}) env = delay_environ_wrapper(env).get() self.assertEqual("Hedgehog hunted.", env["output"]) def test_cascade_job(self): env = create_environ() - env.update(dict(recipe=hunting, area="forest")) + env.update({"recipe": hunting, "area": "forest"}) env = delay_environ_wrapper(env).get() self.assertEqual("Hedgehog hunted.", env["output"]) def test_multiple_jobs(self): - city_result = delay_environ_wrapper(dict(job_id=42, recipe=hunting, area="city")) - forest_result = delay_environ_wrapper(dict(job_id=43, recipe=hunting, area="forest")) - jungle_result = delay_environ_wrapper(dict(job_id=44, recipe=hunting, area="jungle")) + city_result = delay_environ_wrapper({"job_id": 42, "recipe": hunting, "area": "city"}) + forest_result = delay_environ_wrapper({"job_id": 43, "recipe": hunting, "area": "forest"}) + jungle_result = delay_environ_wrapper({"job_id": 44, "recipe": hunting, "area": "jungle"}) self.assertEqual("Hedgehog hunted.", forest_result.get()["output"]) self.assertEqual("Epic fail.", city_result.get()["output"]) self.assertEqual("Epic fail.", jungle_result.get()["output"]) @@ -85,12 +85,12 @@ def upload_source(env, **kwargs): def compile_source(env, **kwargs): env.update( - dict( - source_file=env["remote_source_file"], - out_file=env["binary_file"], - compiler="system-gcc", - job_type="compile", - ) + { + "source_file": env["remote_source_file"], + "out_file": env["binary_file"], + "compiler": "system-gcc", + "job_type": "compile", + } ) return run_sioworkers_job(env) @@ -104,7 +104,7 @@ def upload_inout(env, **kwargs): def run(env, **kwargs): - env.update(dict(exe_file=env["binary_file"], check_output=True, job_type="unsafe-exec")) + env.update({"exe_file": env["binary_file"], "check_output": True, "job_type": "unsafe-exec"}) return run_sioworkers_job(env) @@ -142,17 +142,17 @@ class TestRemoteJobs(TestCase): ("upload test", "oioioi.evalmgr.tests.tests.upload_inout"), ("run", "oioioi.evalmgr.tests.tests.run"), ] - evaluation_env = dict( - job_id=42, - recipe=evaluation_recipe, - local_source_file=local_source_file, - remote_source_file=remote_source_file, - binary_file=binary_file, - local_in_file=local_in_file, - remote_in_file=remote_in_file, - local_out_file=local_out_file, - remote_out_file=remote_out_file, - ) + evaluation_env = { + "job_id": 42, + "recipe": evaluation_recipe, + "local_source_file": local_source_file, + "remote_source_file": remote_source_file, + "binary_file": binary_file, + "local_in_file": local_in_file, + "remote_in_file": remote_in_file, + "local_out_file": local_out_file, + "remote_out_file": remote_out_file, + } def tearDown(self): fc = get_client() @@ -233,35 +233,35 @@ def test_error_behavior(self): case = 1 tests = [ # evaluation error ( - dict(recipe=hunting, area="elevator", error_handlers=self.error_handlers), + {"recipe": hunting, "area": "elevator", "error_handlers": self.error_handlers}, HuntingException, "ARRESTED", "ashamed", ), # job with no recipe ( - dict( - very_important_task="kill another hedgehog remotely", - error_handlers=self.arrest, - ), + { + "very_important_task": "kill another hedgehog remotely", + "error_handlers": self.arrest, + }, RuntimeError, "ARRESTED", None, ), # handler not returning environment ( - dict(recipe=hunting, area="blackhole", error_handlers=self.arrest), + {"recipe": hunting, "area": "blackhole", "error_handlers": self.arrest}, RuntimeError, "ARRESTED", None, ), # corrupted error handler ( - dict( - recipe=hunting, - area="elevator", - error_handlers=self.corrupted_error_handler, - ), + { + "recipe": hunting, + "area": "elevator", + "error_handlers": self.corrupted_error_handler, + }, HuntingException, None, None, diff --git a/oioioi/exportszu/forms.py b/oioioi/exportszu/forms.py index 269b62952..cf0c2de07 100644 --- a/oioioi/exportszu/forms.py +++ b/oioioi/exportszu/forms.py @@ -12,5 +12,5 @@ class ExportSubmissionsForm(forms.Form): only_final = forms.BooleanField(label=_("Only submissions used for final scoring"), required=False, initial=True) def __init__(self, request, *args, **kwargs): - super(ExportSubmissionsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["round"].queryset = request.contest.round_set diff --git a/oioioi/exportszu/utils.py b/oioioi/exportszu/utils.py index 1ce155702..15ddcf802 100644 --- a/oioioi/exportszu/utils.py +++ b/oioioi/exportszu/utils.py @@ -61,7 +61,6 @@ def get_contest_id(self): return self.contest.id def collect_list(self): - ccontroller = self.contest.controller q_expressions = Q(user__isnull=False) if self.round: @@ -75,7 +74,7 @@ def collect_list(self): if self.lang_exts: q_expr_langs = Q() for ext in self.lang_exts: - q_expr_langs |= Q(source_file__contains=".%s@" % ext) + q_expr_langs |= Q(source_file__contains=f".{ext}@") q_expressions &= q_expr_langs if self.only_final: @@ -170,12 +169,7 @@ def encode(obj): index_csv.writerow([encode(col) for col in index_entry]) for s in submission_list: - filename = "%s:%s:%s.%s" % ( - s.submission_id, - s.username, - s.problem_short_name, - s.solution_language, - ) + filename = f"{s.submission_id}:{s.username}:{s.problem_short_name}.{s.solution_language}" dest = os.path.join(files_dir, filename) submission_collector.get_submission_source(dest, s.source_file) diff --git a/oioioi/exportszu/views.py b/oioioi/exportszu/views.py index cb668e40b..b1b3d202f 100644 --- a/oioioi/exportszu/views.py +++ b/oioioi/exportszu/views.py @@ -32,7 +32,7 @@ def export_submissions_view(request): tmp_file.seek(0, os.SEEK_SET) # go to the beginning of the file response = FileResponse(tmp_file) response["Content-Type"] = "application/gzip" - response["Content-Disposition"] = 'attachment; filename="%s.tgz"' % request.contest.id + response["Content-Disposition"] = f'attachment; filename="{request.contest.id}.tgz"' return response else: form = ExportSubmissionsForm(request) diff --git a/oioioi/filetracker/client.py b/oioioi/filetracker/client.py index 38cfdc719..f67daf273 100644 --- a/oioioi/filetracker/client.py +++ b/oioioi/filetracker/client.py @@ -23,10 +23,10 @@ def get_client(): if isinstance(factory, str): factory = import_string(factory) if not callable(factory): - raise ImproperlyConfigured("The FILETRACKER_CLIENT_FACTORY setting refers to non-callable: %r" % (factory,)) + raise ImproperlyConfigured(f"The FILETRACKER_CLIENT_FACTORY setting refers to non-callable: {factory!r}") client = factory() if not isinstance(client, FiletrackerClient): - raise ImproperlyConfigured("The factory pointed by FILETRACKER_CLIENT_FACTORY returned non-FiletrackerClient: %r" % (client,)) + raise ImproperlyConfigured(f"The factory pointed by FILETRACKER_CLIENT_FACTORY returned non-FiletrackerClient: {client!r}") # Needed for oioioi.sioworkers.backends.LocalBackend so that both Django # and sioworkers use the same Filetracker client diff --git a/oioioi/filetracker/fields.py b/oioioi/filetracker/fields.py index dc1b55ec0..c911ba376 100644 --- a/oioioi/filetracker/fields.py +++ b/oioioi/filetracker/fields.py @@ -11,7 +11,7 @@ class FieldFile(files.FieldFile): def __init__(self, instance, field, name): if name is not None: name = FiletrackerFilename(name) - super(FieldFile, self).__init__(instance, field, name) + super().__init__(instance, field, name) def read_using_cache(self): """Opens a file using a cache (if it's possible)""" @@ -23,7 +23,7 @@ def read_using_cache(self): class _FileDescriptor(files.FileDescriptor): def __get__(self, instance=None, owner=None): if instance is None: - raise AttributeError("The '%s' attribute can only be accessed from %s instances." % (self.field.name, owner.__name__)) + raise AttributeError(f"The '{self.field.name}' attribute can only be accessed from {owner.__name__} instances.") file = instance.__dict__[self.field.name] if isinstance(file, str) and file == "none": instance.__dict__[self.field.name] = None @@ -69,12 +69,12 @@ class FileField(files.FileField): def __init__(self, *args, **kwargs): # Default value max_length=100 is not sufficient. kwargs.setdefault("max_length", 255) - super(FileField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def get_prep_value(self, value): if hasattr(value, "name") and isinstance(value.name, FiletrackerFilename): value = value.name.versioned_name - return super(FileField, self).get_prep_value(value) + return super().get_prep_value(value) def value_to_string(self, obj): value = self.value_from_object(obj) diff --git a/oioioi/filetracker/management/commands/collectgarbage.py b/oioioi/filetracker/management/commands/collectgarbage.py index c84778006..f4ea67b5a 100644 --- a/oioioi/filetracker/management/commands/collectgarbage.py +++ b/oioioi/filetracker/management/commands/collectgarbage.py @@ -50,7 +50,7 @@ def handle(self, *args, **options): all_files = get_client().list_local_files() max_date_to_delete = datetime.datetime.now() - datetime.timedelta(days=options["days"]) - diff = set([f[0] for f in all_files]) - set(needed_files) + diff = {f[0] for f in all_files} - set(needed_files) to_delete = [f[0] for f in all_files if f[0] in diff and datetime.datetime.fromtimestamp(f[1]) < max_date_to_delete] files_count = len(to_delete) diff --git a/oioioi/filetracker/utils.py b/oioioi/filetracker/utils.py index 904a8ffc2..e868a487b 100644 --- a/oioioi/filetracker/utils.py +++ b/oioioi/filetracker/utils.py @@ -38,14 +38,14 @@ def django_to_filetracker_path(django_file): """Returns the filetracker path of a :class:`django.core.files.File`.""" storage = getattr(django_file, "storage", None) if not storage: - raise ValueError("File of type %r is not stored in Filetracker" % (type(django_file),)) + raise ValueError(f"File of type {type(django_file)!r} is not stored in Filetracker") name = django_file.name if hasattr(name, "versioned_name"): name = name.versioned_name try: return storage._make_filetracker_path(name) except AttributeError: - raise ValueError("File is stored in %r, not Filetracker" % (storage,)) + raise ValueError(f"File is stored in {storage!r}, not Filetracker") def filetracker_to_django_file(filetracker_path, storage=None): @@ -57,7 +57,7 @@ def filetracker_to_django_file(filetracker_path, storage=None): prefix_len = len(storage.prefix.rstrip("/")) if not filetracker_path.startswith(storage.prefix) or filetracker_path[prefix_len : prefix_len + 1] != "/": - raise ValueError("Path %s is outside of storage prefix %s" % (filetracker_path, storage.prefix)) + raise ValueError(f"Path {filetracker_path} is outside of storage prefix {storage.prefix}") return FileInFiletracker(storage, FiletrackerFilename(filetracker_path[prefix_len + 1 :])) diff --git a/oioioi/forum/admin.py b/oioioi/forum/admin.py index f36038563..1b0c9605d 100644 --- a/oioioi/forum/admin.py +++ b/oioioi/forum/admin.py @@ -21,7 +21,7 @@ def string_concat(*strings): def make_list_elem(elem, text=None): if not text: text = elem.name - return "
  • %s
  • " % make_html_link(elem.get_admin_url(), text) + return f"
  • {make_html_link(elem.get_admin_url(), text)}
  • " def get_permission(self, request): @@ -50,7 +50,7 @@ def categories(self, obj): ret = "".join(slist) if not ret: ret = string_concat("
  • ", _("Empty forum"), "
  • ") - return mark_safe("
      %s
    " % ret) + return mark_safe(f"
      {ret}
    ") categories.short_description = _("Categories") @@ -108,12 +108,12 @@ def response_change(self, request, obj): # view. if "_popup" not in request.POST: return HttpResponseRedirect(request.get_full_path()) - return super(ForumAdmin, self).response_change(request, obj) + return super().response_change(request, obj) def get_queryset(self, request): # each qs filters forum/categories/threads/posts connected with # this particular contest - qs = super(ForumAdmin, self).get_queryset(request) + qs = super().get_queryset(request) qs = qs.filter(contest=request.contest) return qs @@ -130,7 +130,7 @@ def threads(self, obj): ret = "".join(slist) if not ret: ret = string_concat("
  • ", _("Empty category"), "
  • ") - return mark_safe("
      %s
    " % ret) + return mark_safe(f"
      {ret}
    ") threads.short_description = _("Threads") @@ -161,20 +161,20 @@ def has_view_permission(self, request, obj=None): def response_add(self, request, obj, post_url_continue=None): if "_popup" not in request.POST: return HttpResponseRedirect(reverse("oioioiadmin:forum_forum_change", args=(request.contest.forum.id,))) - return super(CategoryAdmin, self).response_add(request, obj, post_url_continue) + return super().response_add(request, obj, post_url_continue) def response_change(self, request, obj): if "_popup" not in request.POST: return HttpResponseRedirect(request.get_full_path()) - return super(CategoryAdmin, self).response_change(request, obj) + return super().response_change(request, obj) def response_delete(self, request): if "came_from" in request.GET or not self.has_change_permission(request): - return super(CategoryAdmin, self).response_delete(request) + return super().response_delete(request) return HttpResponseRedirect(reverse("oioioiadmin:forum_forum_change", args=(request.contest.forum.id,))) def get_queryset(self, request): - qs = super(CategoryAdmin, self).get_queryset(request) + qs = super().get_queryset(request) qs = qs.filter(forum=request.contest.forum) return qs @@ -185,18 +185,18 @@ class ThreadAdmin(admin.ModelAdmin): def get_post_descr(self, post): localtime = timezone.localtime(post.add_date) - return "#%(id)s. %(author)s: %(date)s" % { - "id": post.id, - "author": post.author, - "date": localtime.strftime("%Y-%m-%d %H:%M:%S"), - } + return "#{id}. {author}: {date}".format( + id=post.id, + author=post.author, + date=localtime.strftime("%Y-%m-%d %H:%M:%S"), + ) def posts(self, obj): slist = [make_list_elem(p, self.get_post_descr(p)) for p in obj.post_set.select_related("author").all()] ret = "".join(slist) if not ret: ret = string_concat("
  • ", _("Empty thread"), "
  • ") - return mark_safe("
      %s
    " % ret) + return mark_safe(f"
      {ret}
    ") posts.short_description = _("Posts") @@ -221,25 +221,25 @@ def has_view_permission(self, request, obj=None): def response_add(self, request, obj, post_url_continue=None): if "_popup" not in request.POST: return HttpResponseRedirect(reverse("oioioiadmin:forum_forum_change", args=(request.contest.forum.id,))) - return super(ThreadAdmin, self).response_add(request, obj, post_url_continue) + return super().response_add(request, obj, post_url_continue) def response_change(self, request, obj): if "_popup" not in request.POST: return HttpResponseRedirect(request.get_full_path()) - return super(ThreadAdmin, self).response_change(request, obj) + return super().response_change(request, obj) def response_delete(self, request): if "came_from" in request.GET or not self.has_change_permission(request): - return super(ThreadAdmin, self).response_delete(request) + return super().response_delete(request) return HttpResponseRedirect(reverse("oioioiadmin:forum_forum_change", args=(request.contest.forum.id,))) def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "category": kwargs["queryset"] = Category.objects.filter(forum=request.contest.forum) - return super(ThreadAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) def get_queryset(self, request): - qs = super(ThreadAdmin, self).get_queryset(request) + qs = super().get_queryset(request) qs = qs.filter(category__forum=request.contest.forum) return qs @@ -291,17 +291,17 @@ def has_view_permission(self, request, obj=None): def response_change(self, request, obj): if "_popup" not in request.POST: return HttpResponseRedirect(request.get_full_path()) - return super(PostAdmin, self).response_change(request, obj) + return super().response_change(request, obj) def response_delete(self, request): if "came_from" in request.GET or not self.has_change_permission(request): - return super(PostAdmin, self).response_delete(request) + return super().response_delete(request) return HttpResponseRedirect(reverse("oioioiadmin:forum_forum_change", args=(request.contest.forum.id,))) def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "thread": kwargs["queryset"] = Thread.objects.filter(category__forum=request.contest.forum) - return super(PostAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) def hide_action(self, request, queryset): queryset.update(hidden=True) @@ -363,7 +363,7 @@ def revoke_approval_action(self, request, queryset): revoke_approval_action.short_description = _("Revoke approval of selected posts") def get_queryset(self, request): - qs = super(PostAdmin, self).get_queryset(request) + qs = super().get_queryset(request) qs = qs.filter(thread__category__forum=request.contest.forum) return qs @@ -383,7 +383,7 @@ def has_delete_permission(self, request, obj=None): return get_permission(self, request) def get_queryset(self, request): - qs = super(BanAdmin, self).get_queryset(request) + qs = super().get_queryset(request) qs = qs.filter(forum=request.contest.forum) return qs diff --git a/oioioi/forum/controllers.py b/oioioi/forum/controllers.py index 6ae9e4191..6f1179fb0 100644 --- a/oioioi/forum/controllers.py +++ b/oioioi/forum/controllers.py @@ -12,7 +12,7 @@ class ContestControllerWithForum: create_forum = True def adjust_contest(self): - super(ContestControllerWithForum, self).adjust_contest() + super().adjust_contest() Forum.objects.get_or_create(contest=self.contest) diff --git a/oioioi/forum/forms.py b/oioioi/forum/forms.py index ff8476bf7..1e86d6c20 100644 --- a/oioioi/forum/forms.py +++ b/oioioi/forum/forms.py @@ -12,11 +12,11 @@ class Meta: fields = ["content"] def __init__(self, request, *args, **kwargs): - super(PostForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["content"].widget.attrs["class"] = "monospace" def is_valid(self): - valid = super(PostForm, self).is_valid() + valid = super().is_valid() if not valid: return valid if len(self.cleaned_data["content"]) > getattr(settings, "FORUM_POST_MAX_LENGTH", 20000): @@ -33,7 +33,7 @@ class Meta: content = forms.CharField(widget=forms.Textarea, required=True) def __init__(self, request, *args, **kwargs): - super(NewThreadForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["name"].label = _("Topic") self.fields["name"].widget.attrs["class"] = "monospace" self.fields["content"].widget.attrs["class"] = "monospace" @@ -47,7 +47,7 @@ class Meta: delete_reports = forms.BooleanField(widget=forms.CheckboxInput(), label=_("Remove user reports"), required=False) def __init__(self, *args, **kwargs): - super(BanForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["reason"].label = _("Reason") self.fields["reason"].widget.attrs["class"] = "monospace" @@ -58,7 +58,7 @@ class Meta: fields = ["report_reason"] def __init__(self, *args, **kwargs): - super(ReportForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["report_reason"].label = _("Reason") self.fields["report_reason"].widget.attrs["class"] = "monospace non-resizable" diff --git a/oioioi/forum/models.py b/oioioi/forum/models.py index fb8d86caa..5271db2ae 100644 --- a/oioioi/forum/models.py +++ b/oioioi/forum/models.py @@ -31,7 +31,7 @@ class Meta: verbose_name_plural = _("forums") def __str__(self): - return "%(name)s" % {"name": self.contest.name} + return f"{self.contest.name}" def is_autolocked(self, now=None): """Returns true if forum is locked""" @@ -65,7 +65,7 @@ class Meta: ordering = ("order",) def __str__(self): - return "%s" % self.name + return f"{self.name}" def count_threads(self): return self.thread_set.count() @@ -99,7 +99,7 @@ def save(self, **kwargs): else: self.order = 0 - super(Category, self).save(**kwargs) + super().save(**kwargs) class Thread(models.Model): @@ -121,7 +121,7 @@ class Meta: verbose_name_plural = _("threads") def __str__(self): - return "%(name)s" % {"name": self.name} + return f"{self.name}" def count_posts(self): return self.post_set.count() @@ -194,10 +194,7 @@ class Meta: verbose_name_plural = _("posts") def __str__(self): - return "%(content)s in %(thread)s" % { - "content": self.content, - "thread": self.thread, - } + return f"{self.content} in {self.thread}" def get_admin_url(self): return reverse("oioioiadmin:forum_post_change", args=(self.id,)) @@ -212,7 +209,7 @@ def get_in_thread_url(self): "thread_id": thread.id, }, ) - post_url = "%s#forum-post-%d" % (thread_url, self.id) + post_url = f"{thread_url!s}#forum-post-{self.id}" return post_url def can_be_removed(self): diff --git a/oioioi/forum/tests.py b/oioioi/forum/tests.py index 4d4c45d8b..589567b94 100644 --- a/oioioi/forum/tests.py +++ b/oioioi/forum/tests.py @@ -474,10 +474,7 @@ def test_report(self): ) response = self.client.post(url, follow=True) - reported_pattern = r"was reported\s*by\s*]*>\s*%s %s\s*<\/a>" % ( - name, - surname, - ) + reported_pattern = rf"was reported\s*by\s*]*>\s*{name} {surname}\s*<\/a>" self.assertTrue(re.search(reported_pattern, response.content.decode("utf-8"))) def test_approve_after_report(self): diff --git a/oioioi/globalmessage/admin.py b/oioioi/globalmessage/admin.py index 3c1408cd0..d66671f1b 100644 --- a/oioioi/globalmessage/admin.py +++ b/oioioi/globalmessage/admin.py @@ -25,7 +25,7 @@ def get_urls(self): # Global Message singleton always has primary_key = 1 # @see GlobalMessage.get_singleton pk = "1" - urls = super(GlobalMessageAdmin, self).get_urls() + urls = super().get_urls() custom_urls = [ path("", self.admin_site.admin_view(self.change_view), {"object_id": pk}), diff --git a/oioioi/ipauthsync/admin.py b/oioioi/ipauthsync/admin.py index ddacc454d..293d91808 100644 --- a/oioioi/ipauthsync/admin.py +++ b/oioioi/ipauthsync/admin.py @@ -27,5 +27,5 @@ class ContestAdminWithIpAuthSyncInlineMixin: """ def __init__(self, *args, **kwargs): - super(ContestAdminWithIpAuthSyncInlineMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (IpAuthSyncConfigInline,) diff --git a/oioioi/ipauthsync/controllers.py b/oioioi/ipauthsync/controllers.py index 32cd21506..680005d94 100644 --- a/oioioi/ipauthsync/controllers.py +++ b/oioioi/ipauthsync/controllers.py @@ -9,7 +9,7 @@ class IpAuthSyncControllerMixin: def mixins_for_admin(self): from oioioi.ipauthsync.admin import ContestAdminWithIpAuthSyncInlineMixin - mixins = super(IpAuthSyncControllerMixin, self).mixins_for_admin() + mixins = super().mixins_for_admin() if is_onsite_contest(self.contest): mixins = mixins + (ContestAdminWithIpAuthSyncInlineMixin,) return mixins diff --git a/oioioi/ipauthsync/management/commands/ipauthsyncd.py b/oioioi/ipauthsync/management/commands/ipauthsyncd.py index 5b56cad86..97d064e77 100644 --- a/oioioi/ipauthsync/management/commands/ipauthsyncd.py +++ b/oioioi/ipauthsync/management/commands/ipauthsyncd.py @@ -40,7 +40,7 @@ def handle_config(self, config): try: r = requests.get( f"http://{region.region_server}/ipauthsync/list", - headers=dict(Host="oireg"), + headers={"Host": "oireg"}, ) r.raise_for_status() @@ -53,16 +53,16 @@ def handle_config(self, config): reg = OnsiteRegistration.objects.select_related("participant__user").get(number=zaw_id, region=region) user = reg.participant.user except OnsiteRegistration.DoesNotExist: - warnings.append("* No user found for ZAW=%s (IP=%s)" % (zaw_id, ip)) + warnings.append(f"* No user found for ZAW={zaw_id} (IP={ip})") continue try: rc.ipauthsync_validate_ip(region, ip, user) # pylint: disable=broad-except except Exception as e: - warnings.append("Invalid IP=%s (ZAW=%s): %s" % (ip, zaw_id, e)) + warnings.append(f"Invalid IP={ip} (ZAW={zaw_id}): {e}") - mapping.append("%s %s %s" % (zaw_id, ip, user.username)) + mapping.append(f"{zaw_id} {ip} {user.username}") yield user, ip @@ -73,21 +73,21 @@ def handle_config(self, config): msgs.warnings = warnings msgs.save() mail_admins( - "ipauthsyncd: Warnings for region %s" % (region.short_name,), + f"ipauthsyncd: Warnings for region {region.short_name}", warnings, ) if mapping != msgs.mapping: msgs.mapping = mapping msgs.save() mail_admins( - "ipauthsyncd: Mapping for region %s" % (region.short_name,), + f"ipauthsyncd: Mapping for region {region.short_name}", mapping, ) if region.short_name in self.failing_regions: self.failing_regions.remove(region.short_name) mail_admins( - "ipauthsyncd: Sync now OK for region %s" % (region.short_name,), + f"ipauthsyncd: Sync now OK for region {region.short_name}", "(Intentionally left blank)", ) # pylint: disable=broad-except @@ -96,7 +96,7 @@ def handle_config(self, config): continue self.failing_regions.add(region.short_name) mail_admins( - "ipauthsyncd: Sync failing for region %s" % (region.short_name,), + f"ipauthsyncd: Sync failing for region {region.short_name}", traceback.format_exc(), ) diff --git a/oioioi/ipauthsync/models.py b/oioioi/ipauthsync/models.py index f373c3727..912b23637 100644 --- a/oioioi/ipauthsync/models.py +++ b/oioioi/ipauthsync/models.py @@ -32,7 +32,7 @@ class Meta: verbose_name_plural = _("IP authentication sync configs") def __str__(self): - return "%s (%s): %s - %s" % ( + return "{} ({}): {} - {}".format( self.contest, "enabled" if self.enabled else "disabled", self.start_date, diff --git a/oioioi/ipdnsauth/management/commands/ipauth-dnsserver.py b/oioioi/ipdnsauth/management/commands/ipauth-dnsserver.py index b0949f579..626a74bce 100644 --- a/oioioi/ipdnsauth/management/commands/ipauth-dnsserver.py +++ b/oioioi/ipdnsauth/management/commands/ipauth-dnsserver.py @@ -154,7 +154,7 @@ def add_arguments(self, parser): parser.add_argument("--ttl", type=int, default=60, help="Specify TTL for returned records") def __init__(self, *args, **kwargs): - super(Command, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.options = None def handle(self, *args, **options): diff --git a/oioioi/ipdnsauth/management/commands/ipdnsauth.py b/oioioi/ipdnsauth/management/commands/ipdnsauth.py index 8bcf719c7..db398c894 100644 --- a/oioioi/ipdnsauth/management/commands/ipdnsauth.py +++ b/oioioi/ipdnsauth/management/commands/ipdnsauth.py @@ -72,7 +72,7 @@ def load_data(self, module, modelMgr, data): binding.save() # pylint: disable=broad-except except Exception as e: - self.stderr.write("Error for %s: %s\n" % (row, e)) + self.stderr.write(f"Error for {row}: {e}\n") def load(self, module, modelMgr, filename): self.load_data(module, modelMgr, self._read(filename)) @@ -87,7 +87,7 @@ def unload_data(self, module, modelMgr, data): modelMgr.get(user__username=row[0], dns_name=row[1]).delete() # pylint: disable=broad-except except Exception as e: - self.stderr.write("Error for %s: %s\n" % (row, e)) + self.stderr.write(f"Error for {row}: {e}\n") def unload(self, module, modelMgr, filename): self.unload_data(module, modelMgr, self._read(filename)) diff --git a/oioioi/livedata/tests.py b/oioioi/livedata/tests.py index d9ba3ce2d..9333125f9 100644 --- a/oioioi/livedata/tests.py +++ b/oioioi/livedata/tests.py @@ -41,7 +41,7 @@ def test_cache_unless_admin_or_observer(self): contest = Contest.objects.get() round = Round.objects.filter(contest_id=contest.id).get() view_name = "livedata_teams_view" - cache_key = "%s/%s/%s" % (view_name, contest.id, round.id) + cache_key = f"{view_name}/{contest.id}/{round.id}" url = reverse(view_name, kwargs={"contest_id": contest.id, "round_id": round.id}) # For users that aren't admins or observers, the second request will # come from the cache, this verifies if the cached result's content is diff --git a/oioioi/livedata/utils.py b/oioioi/livedata/utils.py index e74a739a8..ee88aaf8a 100644 --- a/oioioi/livedata/utils.py +++ b/oioioi/livedata/utils.py @@ -8,6 +8,6 @@ def can_see_livedata(request): def get_display_name(user): if user.last_name and user.first_name: - return "%s. %s" % (user.first_name[0], user.last_name) + return f"{user.first_name[0]}. {user.last_name}" else: return user.username diff --git a/oioioi/livedata/views.py b/oioioi/livedata/views.py index b6104257b..cdcbc6700 100644 --- a/oioioi/livedata/views.py +++ b/oioioi/livedata/views.py @@ -26,7 +26,7 @@ def inner(request, round_id): if not should_cache: return view(request, round_id) - cache_key = "%s/%s/%s" % (view.__name__, request.contest.id, round_id) + cache_key = f"{view.__name__}/{request.contest.id}/{round_id}" result = cache.get(cache_key) if result is None: result = view(request, round_id) diff --git a/oioioi/mailsubmit/admin.py b/oioioi/mailsubmit/admin.py index eca32ff66..44edd00ed 100644 --- a/oioioi/mailsubmit/admin.py +++ b/oioioi/mailsubmit/admin.py @@ -38,7 +38,7 @@ class MailSubmissionConfigAdminMixin: """ def __init__(self, *args, **kwargs): - super(MailSubmissionConfigAdminMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.inlines = tuple(self.inlines) + (MailSubmissionConfigInline,) @@ -62,7 +62,7 @@ class MailSubmissionAdmin(admin.ModelAdmin): search_fields = ["user__username", "user__last_name"] def get_custom_list_select_related(self): - return super(MailSubmissionAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "user", "accepted_by", "problem_instance__problem", @@ -118,13 +118,13 @@ def accept_action(self, request, queryset): accept_action.short_description = _("Accept selected submissions") def get_queryset(self, request): - queryset = super(MailSubmissionAdmin, self).get_queryset(request) + queryset = super().get_queryset(request) queryset = queryset.filter(problem_instance__contest=request.contest) queryset = queryset.order_by("-id") return queryset def changelist_view(self, request, extra_context=None): - return super(MailSubmissionAdmin, self).changelist_view(request, extra_context=extra_context) + return super().changelist_view(request, extra_context=extra_context) contest_site.contest_register(MailSubmission, MailSubmissionAdmin) diff --git a/oioioi/mailsubmit/forms.py b/oioioi/mailsubmit/forms.py index 5f4eb36cb..3058308e3 100644 --- a/oioioi/mailsubmit/forms.py +++ b/oioioi/mailsubmit/forms.py @@ -14,10 +14,10 @@ class MailSubmissionForm(SubmissionForm): def __init__(self, request, *args, **kwargs): self.kind = "MAILSUBMIT" - super(MailSubmissionForm, self).__init__(request, *args, add_kind_and_user_fields=False, **kwargs) + super().__init__(request, *args, add_kind_and_user_fields=False, **kwargs) def clean(self): - cleaned_data = super(MailSubmissionForm, self).clean(check_submission_limit=False, check_round_times=False) + cleaned_data = super().clean(check_submission_limit=False, check_round_times=False) decision = is_mailsubmit_allowed(self.request) if not decision: @@ -39,11 +39,11 @@ class AcceptMailSubmissionForm(forms.Form): ) def __init__(self, request, *args, **kwargs): - super(AcceptMailSubmissionForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.request = request def clean(self): - cleaned_data = super(AcceptMailSubmissionForm, self).clean() + cleaned_data = super().clean() if "mailsubmission_id" not in cleaned_data or "submission_hash" not in cleaned_data: return cleaned_data diff --git a/oioioi/mailsubmit/models.py b/oioioi/mailsubmit/models.py index 0f3793569..5d003c611 100644 --- a/oioioi/mailsubmit/models.py +++ b/oioioi/mailsubmit/models.py @@ -16,11 +16,7 @@ def make_submission_filename(instance, filename): if not instance.id: instance.save() - return "mailsubmissions/%s/%d%s" % ( - instance.problem_instance.contest.id, - instance.id, - os.path.splitext(filename)[1], - ) + return f"mailsubmissions/{instance.problem_instance.contest.id!s}/{instance.id}{os.path.splitext(filename)[1]!s}" class MailSubmission(models.Model): diff --git a/oioioi/mailsubmit/utils.py b/oioioi/mailsubmit/utils.py index f51ee806d..c70ba897f 100644 --- a/oioioi/mailsubmit/utils.py +++ b/oioioi/mailsubmit/utils.py @@ -66,7 +66,7 @@ def mail_submission_hashes(mailsubmission): pi = mailsubmission.problem_instance - msg = "%d-%s-%d-%s" % (mailsubmission.id, pi.contest.id, pi.id, source_hash) + msg = f"{mailsubmission.id}-{pi.contest.id}-{pi.id}-{source_hash}" msg = msg.encode("utf-8") submission_hash = hmac.new( diff --git a/oioioi/mailsubmit/views.py b/oioioi/mailsubmit/views.py index 3ae29ca82..52d367d80 100644 --- a/oioioi/mailsubmit/views.py +++ b/oioioi/mailsubmit/views.py @@ -113,7 +113,7 @@ def _generate_pdfdoc(request, mailsubmission): }, ) - filename = "%s-%s-%s.pdf" % ( + filename = "{}-{}-{}.pdf".format( _("confirmation"), mailsubmission.problem_instance.short_name, mailsubmission.id, diff --git a/oioioi/mp/admin.py b/oioioi/mp/admin.py index d2ca4e34e..47484bf1e 100644 --- a/oioioi/mp/admin.py +++ b/oioioi/mp/admin.py @@ -30,7 +30,7 @@ def has_delete_permission(self, request, obj=None): return request.user.is_superuser def get_actions(self, request): - actions = super(MPRegistrationParticipantAdmin, self).get_actions(request) + actions = super().get_actions(request) if "delete_selected" in actions: del actions["delete_selected"] return actions diff --git a/oioioi/mp/controllers.py b/oioioi/mp/controllers.py index 21a9447a3..0b7f2f3b8 100644 --- a/oioioi/mp/controllers.py +++ b/oioioi/mp/controllers.py @@ -89,7 +89,7 @@ def registration_view(self, request): def mixins_for_admin(self): from oioioi.participants.admin import TermsAcceptedPhraseAdminMixin - return super(MPRegistrationController, self).mixins_for_admin() + (TermsAcceptedPhraseAdminMixin,) + return super().mixins_for_admin() + (TermsAcceptedPhraseAdminMixin,) def can_change_terms_accepted_phrase(self, request): return not MPRegistration.objects.filter(participant__contest=request.contest).exists() @@ -185,7 +185,7 @@ def can_submit(self, request, problem_instance, check_round_times=True): contest=problem_instance.contest, end_date__gte=request.timestamp, ) - return super(MPContestController, self).can_submit(request, problem_instance, check_round_times) or round_over_contest_running + return super().can_submit(request, problem_instance, check_round_times) or round_over_contest_running class MPRankingController(DefaultRankingController): diff --git a/oioioi/mp/forms.py b/oioioi/mp/forms.py index d5085a45b..baf6767b4 100644 --- a/oioioi/mp/forms.py +++ b/oioioi/mp/forms.py @@ -38,7 +38,7 @@ class Meta: exclude = ["participant"] def __init__(self, *args, **kwargs): - super(MP2025RegistrationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) this_year = datetime.date.today().year self.fields["birth_year"].validators.extend( diff --git a/oioioi/mp/score.py b/oioioi/mp/score.py index 508a66db1..a35695bc3 100644 --- a/oioioi/mp/score.py +++ b/oioioi/mp/score.py @@ -40,14 +40,14 @@ def __unicode__(self): return str(self.value) def __repr__(self): - return "FloatScore(%s)" % (self.value,) + return f"FloatScore({self.value})" @classmethod def _from_repr(cls, value): return cls(float(value)) def _to_repr(self): - return "%017.2f" % self.value + return f"{self.value:017.2f}" def to_int(self): return int(self.value) diff --git a/oioioi/mp/tests.py b/oioioi/mp/tests.py index d721fd816..f6078c751 100644 --- a/oioioi/mp/tests.py +++ b/oioioi/mp/tests.py @@ -33,7 +33,7 @@ def _check_order(self, response, expected): self.assertTrue(pattern_match) pos = pattern_match.start() - self.assertGreater(pos, prev_pos, msg=("Round %s has incorrect position" % (round_name,))) + self.assertGreater(pos, prev_pos, msg=(f"Round {round_name} has incorrect position")) prev_pos = pos def test_rounds_order(self): diff --git a/oioioi/newsfeed/models.py b/oioioi/newsfeed/models.py index 96372c4c6..e1026a6d7 100644 --- a/oioioi/newsfeed/models.py +++ b/oioioi/newsfeed/models.py @@ -47,4 +47,4 @@ def save(self, *args, **kwargs): except NewsLanguageVersion.DoesNotExist: pass - return super(NewsLanguageVersion, self).save(*args, **kwargs) + return super().save(*args, **kwargs) diff --git a/oioioi/notifications/processors.py b/oioioi/notifications/processors.py index 70a06d8c1..b6bdba6ba 100644 --- a/oioioi/notifications/processors.py +++ b/oioioi/notifications/processors.py @@ -37,10 +37,10 @@ def generator(): notifications_session_id = get_notifications_session(request.session).uid return render_to_string( "notifications/notifications.html", - dict( - notif_server_url=settings.NOTIFICATIONS_SERVER_URL, - notifications_session_id=notifications_session_id, - ), + { + "notif_server_url": settings.NOTIFICATIONS_SERVER_URL, + "notifications_session_id": notifications_session_id, + }, ) return {"extra_navbar_right_notifications": lazy(generator, str)()} diff --git a/oioioi/oi/admin.py b/oioioi/oi/admin.py index 0be003ab1..7705acca2 100644 --- a/oioioi/oi/admin.py +++ b/oioioi/oi/admin.py @@ -96,7 +96,7 @@ def get_all_related_objects(modelObj): # http://stackoverflow.com/questions/3393378/django-merging-objects related = get_all_related_objects(approved) - valnames = dict() + valnames = {} for r in related: valnames.setdefault(r.related_model, []).append(r.field.name) @@ -145,7 +145,7 @@ class OIRegistrationParticipantAdmin(ParticipantAdmin): list_filter = ParticipantAdmin.list_filter + ["oi_oiregistration__school__province"] def get_custom_list_select_related(self): - return super(OIRegistrationParticipantAdmin, self).get_custom_list_select_related() + [ + return super().get_custom_list_select_related() + [ "oi_oiregistration", "oi_oiregistration__school", ] @@ -179,7 +179,7 @@ def has_delete_permission(self, request, obj=None): return request.user.is_superuser def get_actions(self, request): - actions = super(OIRegistrationParticipantAdmin, self).get_actions(request) + actions = super().get_actions(request) if "delete_selected" in actions: del actions["delete_selected"] return actions diff --git a/oioioi/oi/controllers.py b/oioioi/oi/controllers.py index 90177f17f..e1d0d49e1 100644 --- a/oioioi/oi/controllers.py +++ b/oioioi/oi/controllers.py @@ -107,7 +107,7 @@ def registration_view(self, request): return TemplateResponse(request, self.registration_template, context) def get_contest_participant_info_list(self, request, user): - prev = super(OIRegistrationController, self).get_contest_participant_info_list(request, user) + prev = super().get_contest_participant_info_list(request, user) if can_see_personal_data(request): sensitive_info = OIRegistration.objects.filter(participant__user=user, participant__contest=request.contest) @@ -125,7 +125,7 @@ def get_contest_participant_info_list(self, request, user): def mixins_for_admin(self): from oioioi.participants.admin import TermsAcceptedPhraseAdminMixin - return super(OIRegistrationController, self).mixins_for_admin() + (TermsAcceptedPhraseAdminMixin,) + return super().mixins_for_admin() + (TermsAcceptedPhraseAdminMixin,) def can_change_terms_accepted_phrase(self, request): return not OIRegistration.objects.filter(participant__contest=request.contest).exists() @@ -145,7 +145,7 @@ class OIContestController(ProgrammingContestController): ) def fill_evaluation_environ(self, environ, submission): - super(OIContestController, self).fill_evaluation_environ(environ, submission) + super().fill_evaluation_environ(environ, submission) environ["group_scorer"] = "oioioi.programs.utils.min_group_scorer" environ["test_scorer"] = "oioioi.programs.utils.threshold_linear_test_scorer" @@ -160,7 +160,7 @@ def can_submit(self, request, problem_instance, check_round_times=True): return True if not is_participant(request): return False - return super(OIContestController, self).can_submit(request, problem_instance, check_round_times) + return super().can_submit(request, problem_instance, check_round_times) def can_see_stats(self, request): return is_contest_admin(request) or is_contest_observer(request) @@ -194,10 +194,10 @@ def default_can_see_ranking(self, request): return is_contest_admin(request) or is_contest_observer(request) def default_contestlogo_url(self): - return "%(url)soi/logo.png" % {"url": settings.STATIC_URL} + return f"{settings.STATIC_URL}oi/logo.png" def default_contesticons_urls(self): - return ["%(url)simages/menu/menu-icon-%(i)d.png" % {"url": settings.STATIC_URL, "i": i} for i in range(1, 4)] + return [f"{settings.STATIC_URL}images/menu/menu-icon-{i}.png" for i in range(1, 4)] class OIOnsiteContestController(OIContestController): @@ -264,7 +264,7 @@ def can_see_test_comments(self, request, submissionreport): return is_contest_admin(request) or self.results_visible(request, submission) def reveal_score(self, request, submission): - super(BOIOnsiteContestController, self).reveal_score(request, submission) + super().reveal_score(request, submission) self.update_user_results(submission.user, submission.problem_instance) def update_user_result_for_problem(self, result): @@ -314,7 +314,7 @@ def default_contesticons_urls(self): return [] def fill_evaluation_environ(self, environ, submission): - super(BOIOnsiteContestController, self).fill_evaluation_environ(environ, submission) + super().fill_evaluation_environ(environ, submission) environ["test_scorer"] = "oioioi.programs.utils.discrete_test_scorer" diff --git a/oioioi/oi/forms.py b/oioioi/oi/forms.py index 0d20020f4..3586a5288 100644 --- a/oioioi/oi/forms.py +++ b/oioioi/oi/forms.py @@ -24,14 +24,14 @@ def city_options(all_schools, province): def school_options(all_schools, province, city): schools = all_schools.filter(province=province, city=city).order_by("name").only("name", "address") - schools = [(s.id, "%s (%s)" % (s.name, s.address)) for s in schools] + schools = [(s.id, f"{s.name} ({s.address})") for s in schools] schools.insert(0, ("", _("-- Choose school --"))) return schools class SchoolSelect(forms.Select): def __init__(self, is_contest_with_coordinator=False, is_coordinator=False, *args, **kwargs): - super(SchoolSelect, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.is_contest_with_coordinator = is_contest_with_coordinator self.is_coordinator = is_coordinator @@ -85,7 +85,7 @@ class Media: js = ("oi/reg.js",) def __init__(self, *args, **kwargs): - super(OIRegistrationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) this_year = datetime.date.today().year years = list(reversed(range(this_year - 100, this_year + 1))) diff --git a/oioioi/oi/management/commands/import_schools.py b/oioioi/oi/management/commands/import_schools.py index dfba61bfe..41ab60ceb 100644 --- a/oioioi/oi/management/commands/import_schools.py +++ b/oioioi/oi/management/commands/import_schools.py @@ -350,7 +350,7 @@ def handle(self, *args, **kwargs): DRY_RUN = dry_run first_import = kwargs["first_import"] backup_path = kwargs["backup_filename"] - VERBOSITY = kwargs["verbosity"] + kwargs["verbosity"] prepare_dir(BASE_DIR) diff --git a/oioioi/oi/management/commands/oi_generate_dnsauth.py b/oioioi/oi/management/commands/oi_generate_dnsauth.py index 3ecbb48ab..372c1fdbb 100644 --- a/oioioi/oi/management/commands/oi_generate_dnsauth.py +++ b/oioioi/oi/management/commands/oi_generate_dnsauth.py @@ -11,7 +11,7 @@ def username_to_dns_name(username): hostname = username_to_hostname(username) - return "oi-%s.dasie.mimuw.edu.pl" % (hostname,) + return f"oi-{hostname}.dasie.mimuw.edu.pl" class Command(BaseCommand): diff --git a/oioioi/oireports/forms.py b/oioioi/oireports/forms.py index 9d0f2b469..9339a9559 100644 --- a/oioioi/oireports/forms.py +++ b/oioioi/oireports/forms.py @@ -70,7 +70,7 @@ def _testgroups(request): class OIReportCheckboxSelectMultiple(forms.CheckboxSelectMultiple): def render(self, *args, **kwargs): - output = super(OIReportCheckboxSelectMultiple, self).render(*args, **kwargs) + output = super().render(*args, **kwargs) return mark_safe(output.replace("