diff --git a/oioioi/contests/handlers.py b/oioioi/contests/handlers.py index b356552b9..c50be6bc6 100644 --- a/oioioi/contests/handlers.py +++ b/oioioi/contests/handlers.py @@ -4,10 +4,12 @@ import pprint import socket import time +from functools import wraps from smtplib import SMTPException from django.core.mail import mail_admins from django.db import transaction -from oioioi.contests.models import Contest, ProblemInstance, Submission, \ +from oioioi.base.utils.db import require_transaction +from oioioi.contests.models import ProblemInstance, Submission, \ SubmissionReport, FailureReport logger = logging.getLogger(__name__) @@ -17,6 +19,33 @@ WAIT_FOR_SUBMISSION_SLEEP_SECONDS = 1 +#TODO: Improve after migration to Python 3: +# def _get_submission_or_skip(*args, submission_class=Submission) +def _get_submission_or_skip(*args, **kwargs): + submission_class = kwargs.get('submission_class', Submission) + + def wrapper(fn): + """A decorator which tries to get a submission by id from env or skips + the decorated function if the submission doesn't exist. + """ + @wraps(fn) + @require_transaction + def decorated(env, *args, **kwargs): + if 'submission_id' not in env: + return env + try: + submission = submission_class.objects.get( + id=env['submission_id']) + except Submission.DoesNotExist: + return env + return fn(env, submission, *args, **kwargs) + return decorated + + if len(args) == 1: + return wrapper(args[0]) + return wrapper + + def wait_for_submission_in_db(env, **kwargs): """Celery may start handling a submission before it is actually saved in the DB. This is a workaround for this. @@ -30,8 +59,8 @@ def wait_for_submission_in_db(env, **kwargs): @transaction.atomic -def update_report_statuses(env, **kwargs): - submission = Submission.objects.get(id=env['submission_id']) +@_get_submission_or_skip +def update_report_statuses(env, submission, **kwargs): problem_instance = submission.problem_instance reports = SubmissionReport.objects.filter(submission=submission) problem_instance.controller.update_report_statuses(submission, reports) @@ -39,8 +68,8 @@ def update_report_statuses(env, **kwargs): @transaction.atomic -def update_submission_score(env, **kwargs): - submission = Submission.objects.get(id=env['submission_id']) +@_get_submission_or_skip +def update_submission_score(env, submission, **kwargs): problem_instance = submission.problem_instance problem_instance.controller.update_submission_score(submission) return env @@ -48,7 +77,11 @@ def update_submission_score(env, **kwargs): def update_user_results(env, **kwargs): with transaction.atomic(): - submission = Submission.objects.get(id=env['submission_id']) + try: + submission = Submission.objects.get(id=env['submission_id']) + except Submission.DoesNotExist: + return env + user = submission.user if not user: return env @@ -70,8 +103,8 @@ def update_user_results(env, **kwargs): @transaction.atomic -def call_submission_judged(env, **kwargs): - submission = Submission.objects.get(id=env['submission_id']) +@_get_submission_or_skip +def call_submission_judged(env, submission, **kwargs): contest = submission.problem_instance.contest if contest is None: @@ -85,7 +118,8 @@ def call_submission_judged(env, **kwargs): @transaction.atomic -def create_error_report(env, exc_info, **kwargs): +@_get_submission_or_skip +def create_error_report(env, submission, exc_info, **kwargs): """Builds a :class:`oioioi.contests.models.SubmissionReport` for an evaulation which have failed. @@ -97,14 +131,6 @@ def create_error_report(env, exc_info, **kwargs): env.get('submission_id', '???'), pprint.pformat(env, indent=4), exc_info=exc_info) - if 'submission_id' not in env: - return env - - try: - submission = Submission.objects.get(id=env['submission_id']) - except Submission.DoesNotExist: - return env - submission_report = SubmissionReport(submission=submission) submission_report.kind = 'FAILURE' submission_report.save() @@ -117,7 +143,9 @@ def create_error_report(env, exc_info, **kwargs): return env -def mail_admins_on_error(env, exc_info, **kwargs): +@transaction.atomic +@_get_submission_or_skip +def mail_admins_on_error(env, submission, exc_info, **kwargs): """Sends email to all admins defined in settings.ADMINS on each grading error occurrence. @@ -125,14 +153,6 @@ def mail_admins_on_error(env, exc_info, **kwargs): * `env['submission_id']` """ - # We don't want to spam admins when the evaluation of a deleted - # submission fails. See also SIO-1254. - try: - if 'submission_id' in env: - Submission.objects.get(id=env['submission_id']) - except Submission.DoesNotExist: - return env - try: mail_admins("System Error evaluating submission #%s" % env.get('submission_id', '???'), diff --git a/oioioi/programs/handlers.py b/oioioi/programs/handlers.py index 3f8123d6b..5ae8fe812 100755 --- a/oioioi/programs/handlers.py +++ b/oioioi/programs/handlers.py @@ -15,6 +15,7 @@ from oioioi.contests.scores import ScoreValue, IntegerScore from oioioi.contests.models import Submission, SubmissionReport, \ ScoreReport +from oioioi.contests.handlers import _get_submission_or_skip from oioioi.programs.models import CompilationReport, TestReport, \ GroupReport, Test, UserOutGenStatus from oioioi.filetracker.client import get_client @@ -417,7 +418,8 @@ def grade_submission(env, kind='NORMAL', **kwargs): return env -def _make_base_report(env, kind): +@_get_submission_or_skip +def _make_base_report(env, submission, kind): """Helper function making: SubmissionReport, ScoreReport, CompilationReport. @@ -435,7 +437,6 @@ def _make_base_report(env, kind): Returns: tuple (submission, submission_report) """ - submission = Submission.objects.get(id=env['submission_id']) submission_report = SubmissionReport(submission=submission) submission_report.kind = kind submission_report.save() @@ -591,7 +592,8 @@ def fill_outfile_in_existing_test_reports(env, **kwargs): @transaction.atomic -def insert_existing_submission_link(env, **kwargs): +@_get_submission_or_skip +def insert_existing_submission_link(env, src_submission, **kwargs): """Add comment to some existing submission with link to submission view of present submission. @@ -607,7 +609,6 @@ def insert_existing_submission_link(env, **kwargs): submission_report_id = env['extra_args']['submission_report_id'] submission_report = SubmissionReport.objects.get(id=submission_report_id) dst_submission = submission_report.submission - src_submission = Submission.objects.get(id=env['submission_id']) href = reverse('submission', kwargs={'submission_id': dst_submission.id, 'contest_id': env['contest_id']}) html_link = make_html_link(href, _("submission report") + ": " + diff --git a/oioioi/suspendjudge/handlers.py b/oioioi/suspendjudge/handlers.py index 8d2684032..788cbbff7 100644 --- a/oioioi/suspendjudge/handlers.py +++ b/oioioi/suspendjudge/handlers.py @@ -3,6 +3,7 @@ from django.db import transaction from oioioi.contests.models import Submission +from oioioi.contests.handlers import _get_submission_or_skip from oioioi.programs.models import ModelProgramSubmission from oioioi.suspendjudge.models import SuspendedProblem from oioioi.evalmgr.utils import mark_job_state @@ -22,11 +23,11 @@ def _is_hidden_rejudge(env): return env['is_rejudge'] and 'HIDDEN' in env['report_kinds'] -def _is_admin_submission(env): - s = Submission.objects.get(pk=env['submission_id']) - if s.user is not None: - return s.user.has_perm('contests.contest_admin', - s.problem_instance.contest) +@_get_submission_or_skip +def _is_admin_submission(env, submission): + if submission.user is not None: + return submission.user.has_perm('contests.contest_admin', + submission.problem_instance.contest) return False diff --git a/oioioi/testrun/handlers.py b/oioioi/testrun/handlers.py index 1fd803c7f..77ea4264c 100644 --- a/oioioi/testrun/handlers.py +++ b/oioioi/testrun/handlers.py @@ -1,7 +1,8 @@ from django.db import transaction from django.utils.text import Truncator -from oioioi.programs.handlers import _skip_on_compilation_error, _make_base_report +from oioioi.programs.handlers import _skip_on_compilation_error, \ + _make_base_report, _get_submission_or_skip from oioioi.testrun.models import TestRunProgramSubmission, TestRunConfig, \ TestRunReport from oioioi.filetracker.utils import django_to_filetracker_path, \ @@ -11,7 +12,8 @@ @_skip_on_compilation_error @transaction.atomic -def make_test(env, **kwargs): +@_get_submission_or_skip(submission_class=TestRunProgramSubmission) +def make_test(env, submission, **kwargs): """Creates a testcase *test* from the user input and converts it to evaluation environment. @@ -22,7 +24,6 @@ def make_test(env, **kwargs): Produced ``environ`` keys: * ``tests``: a dictionary mapping test names to test envs """ - submission = TestRunProgramSubmission.objects.get(id=env['submission_id']) assert submission.kind == 'TESTRUN' config = TestRunConfig.objects.get(problem__id=env['problem_id']) diff --git a/oioioi/zeus/handlers.py b/oioioi/zeus/handlers.py index fd50563c0..c740ca2e2 100644 --- a/oioioi/zeus/handlers.py +++ b/oioioi/zeus/handlers.py @@ -12,6 +12,7 @@ from oioioi import evalmgr from oioioi.base.utils import naturalsort_key +from oioioi.contests.handlers import _get_submission_or_skip from oioioi.programs.handlers import _skip_on_compilation_error from oioioi.programs.models import ProgramSubmission, Test from oioioi.zeus.backends import get_zeus_server @@ -41,10 +42,10 @@ def from_csv_metadata(metadata): @_skip_on_compilation_error @transaction.atomic -def submit_job(env, kind): +@_get_submission_or_skip(submission_class=ProgramSubmission) +def submit_job(env, submission, kind): """Recipe handler that sends the job to Zeus. """ - submission = ProgramSubmission.objects.get(id=env['submission_id']) with submission.source_file as f: source_code = f.read() return evalmgr.transfer_job(env,