Пример #1
0
    def test_handler_presence(self):
        contest = Contest.objects.get()
        submission = Submission.objects.get()
        controller = ProgrammingContestController(contest)

        env = create_environ()
        env.setdefault('recipe', []).append(('dummy', 'dummy'))
        env['extra_args'] = []
        controller.fill_evaluation_environ(env, submission)
        controller.finalize_evaluation_environment(env)

        self.assertIn(('check_problem_instance_state',
                'oioioi.suspendjudge.handlers.check_problem_instance_state',
                dict(suspend_init_tests=True)), env['recipe'])
        self.assertIn(('check_problem_instance_state',
                'oioioi.suspendjudge.handlers.check_problem_instance_state'),
                env['recipe'])
Пример #2
0
    def test_handler_presence(self):
        contest = Contest.objects.get()
        submission = Submission.objects.get()
        controller = ProgrammingContestController(contest)

        env = create_environ()
        env.setdefault('recipe', []).append(('dummy', 'dummy'))
        env['extra_args'] = []
        controller.fill_evaluation_environ(env, submission)
        controller.finalize_evaluation_environment(env)

        self.assertIn(
            (
                'check_problem_instance_state',
                'oioioi.suspendjudge.handlers.check_problem_instance_state',
                dict(suspend_init_tests=True),
            ),
            env['recipe'],
        )
        self.assertIn(
            (
                'check_problem_instance_state',
                'oioioi.suspendjudge.handlers.check_problem_instance_state',
            ),
            env['recipe'],
        )
Пример #3
0
    def test_handler_presence(self):
        contest = Contest.objects.get()
        submission = Submission.objects.get()
        controller = ProgrammingContestController(contest)

        env = {}
        env["recipe"] = [("dummy", "dummy")]
        env["postpone_handlers"] = [("dummy", "dummy")]
        env["extra_args"] = []
        controller.fill_evaluation_environ(env, submission)
        controller.finalize_evaluation_environment(env)

        self.assertIn(
            (
                "check_problem_instance_state",
                "oioioi.suspendjudge.handlers.check_problem_instance_state",
                dict(suspend_init_tests=True),
            ),
            env["recipe"],
        )
        self.assertIn(
            ("check_problem_instance_state", "oioioi.suspendjudge.handlers.check_problem_instance_state"), env["recipe"]
        )
Пример #4
0
                              timedelta(minutes=disable_time)) \
            and rtimes.is_active(request.timestamp)

    def render_submission_footer(self, request, submission):
        super_footer = super(ScoresRevealContestControllerMixin, self). \
                render_submission_footer(request, submission)

        if not has_scores_reveal(submission.problem) or \
                submission.kind != 'NORMAL' or submission.user is None:
            return super_footer

        scores_reveals = self.get_revealed_submissions(submission.user,
            submission.problem_instance).count()
        scores_reveals_limit = self.get_scores_reveals_limit(
            submission.problem_instance)
        scores_reveals_disable_time = self.get_scores_reveals_disable_time(
            submission.problem_instance)
        can_reveal, reason = self.can_reveal(request, submission)

        return render_to_string('scoresreveal/submission_footer.html',
            context_instance=RequestContext(request,
                {'submission': submission_template_context(request,
                 submission.programsubmission),
                 'scores_reveals': scores_reveals,
                 'scores_reveals_limit': scores_reveals_limit,
                 'scores_reveals_disable_time': scores_reveals_disable_time,
                 'can_reveal': can_reveal,
                 'can_reveal_reason': reason})) + super_footer

ProgrammingContestController.mix_in(ScoresRevealContestControllerMixin)
Пример #5
0
        super(SubmitsQueueContestControllerMixin, self).\
            finalize_evaluation_environment(environ)
        environ['recipe'].insert(0, (
                'mark_submission_in_progress',
                'oioioi.submitsqueue.handlers.mark_submission_state',
                dict(state='PROGRESS')))
        if 'postpone_handlers' not in environ:
            environ['postpone_handlers'] = []
        environ['postpone_handlers'].append(('update_celery_task_id',
                'oioioi.submitsqueue.handlers.update_celery_task_id'))
        if 'error_handlers' not in environ:
            environ['error_handlers'] = []
        environ['error_handlers'].insert(0, (
                'remove_submission_on_error',
                'oioioi.submitsqueue.handlers.remove_submission_on_error'))

    def submission_queued(self, submission, async_result):
        super(SubmitsQueueContestControllerMixin, self).\
            submission_queued(submission, async_result)
        QueuedSubmit.objects.get_or_create(submission=submission,
                                           celery_task_id=async_result.id)

    def submission_unqueued(self, submission, job_id):
        super(SubmitsQueueContestControllerMixin, self).\
            submission_unqueued(submission, job_id)
        QueuedSubmit.objects.filter(submission=submission,
                                    celery_task_id=job_id).delete()


ProgrammingContestController.mix_in(SubmitsQueueContestControllerMixin)
Пример #6
0
        """This fuction takes for a parameter a queryset
           with submissions for published rounds.

           It should return a filtered queryset with exactly these submissions
           for which solution is mandatorily public.
        """
        return qs.none()

    def solutions_may_be_published(self, qs):
        """This fuction takes for a parameter a queryset
           with submissions for published rounds.

           It should return a filtered queryset with exactly these submissions
           that a user can decide themself to (un)publish.

           You can assume that none of given submissions meets
           :meth:'solutions_must_be_public' predicate.

           At the start these submission are unpublished.
        """
        return qs.none()

    def filter_visible_sources(self, request, qs):
        prev = super(PublicSolutionsContestControllerMixin, self) \
                .filter_visible_sources(request, qs)

        public = filter_public_solutions(request, qs)
        return (prev | public).distinct()

ProgrammingContestController.mix_in(PublicSolutionsContestControllerMixin)
Пример #7
0
            # Do not split this filter as it spans many-to-many relationship
            q_expression = Q(submissions__submission=submission,
                             submissions__guilty=True)
        similarities = SubmissionsSimilarityGroup.objects \
            .filter(q_expression) \
            .prefetch_related('submissions')
        if not similarities:
            return prev

        submission_contexts = {}
        for group in similarities:
            for entry in group.submissions.all():
                submission_contexts[entry.submission] = \
                        submission_template_context(request, entry.submission)

        template = ('similarsubmits/programming_similar_submissions_admin.html'
                    if is_contest_admin(request) else
                    'similarsubmits/programming_similar_submissions.html')

        context = RequestContext(
            request, {
                'similarities': similarities,
                'main_submission_id': submission.id,
                'submission_contexts': submission_contexts,
            })

        return prev + render_to_string(template, context_instance=context)


ProgrammingContestController.mix_in(SimilarityDisqualificationMixin)
Пример #8
0
    """ContestController mixin that renders submission disqualification info.
    """

    def render_submission(self, request, submission):
        prev = super(DisqualificationProgrammingContestControllerMixin, self) \
            .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)
        return prev


ProgrammingContestController.mix_in(
    DisqualificationProgrammingContestControllerMixin)


class WithDisqualificationRankingControllerMixin(object):
    """RankingController mixin that manages disqualification module influence
       on rankings.
    """

    def _show_disqualified(self, key):
        """Decides if disqualified users should be included in the ranking.

           They will be marked as disqualified and will *not* influence the
           places of other contestants.
        """
        return self.is_admin_key(key)
Пример #9
0
from oioioi.questions.forms import FilterMessageForm
from oioioi.questions.management.commands.mailnotifyd import (
    candidate_messages, mailnotify)
from oioioi.questions.models import Message, ReplyTemplate
from oioioi.questions.utils import unanswered_questions

from .views import visible_messages


class TestContestControllerMixin(object):
    def users_to_receive_public_message_notification(self):
        return self.registration_controller().filter_participants(
            User.objects.all())


ProgrammingContestController.mix_in(TestContestControllerMixin)


class TestQuestions(TestCase):
    fixtures = [
        'test_users', 'test_contest', 'test_full_package',
        'test_problem_instance', 'test_messages', 'test_templates',
        'test_subscriptions'
    ]

    def test_visibility(self):
        contest = Contest.objects.get()
        all_messages = [
            'problem-question', 'contest-question', 'public-answer',
            'private-answer'
        ]
        scores_reveals = self.get_revealed_submissions(
            submission.user, submission.problem_instance).count()
        scores_reveals_limit = self.get_scores_reveals_limit(
            submission.problem_instance)
        scores_reveals_disable_time = self.get_scores_reveals_disable_time(
            submission.problem_instance)
        can_reveal, reason = self.can_reveal(request, submission)

        return render_to_string(
            'scoresreveal/submission_footer.html',
            context_instance=RequestContext(
                request, {
                    'submission':
                    submission_template_context(request,
                                                submission.programsubmission),
                    'scores_reveals':
                    scores_reveals,
                    'scores_reveals_limit':
                    scores_reveals_limit,
                    'scores_reveals_disable_time':
                    scores_reveals_disable_time,
                    'can_reveal':
                    can_reveal,
                    'can_reveal_reason':
                    reason
                })) + super_footer


ProgrammingContestController.mix_in(ScoresRevealContestControllerMixin)
Пример #11
0
class CodeSharingContestControllerMixin(object):
    """A mixin allowing users to see shared submissions' contents.
    """
    def filter_visible_sources(self, request, queryset):
        prev = super(CodeSharingContestControllerMixin, self) \
                .filter_visible_sources(request, queryset)

        if not request.user.is_authenticated():
            return prev

        shared = CodeSharingController().all_shared_with_me(request.user)
        return prev | queryset.filter(id__in=shared)


ProgrammingContestController.mix_in(CodeSharingContestControllerMixin)


class ProblemExperienceMixinForContestController(object):
    """A helper mixin for oioioi.gamification.ProblemExperienceSource."""
    def submission_judged(self, submission, rejudged=False):
        super(ProblemExperienceMixinForContestController, self)\
                .submission_judged(submission, rejudged)

        try:
            report = SubmissionReport.objects.get(
                userresultforproblem__user=submission.user,
                userresultforproblem__problem_instance=
                    submission.problem_instance
            )
Пример #12
0
            result.append(plot_kind('SUBMISSIONS_HISTOGRAM_CONTEST', object))

            pis = visible_problem_instances(request)

            # Do not add this plot if it would be empty.
            if any(pi_results_visible(pi) and not pi.round.is_trial
                   for pi in pis):
                result.append(plot_kind('POINTS_HISTOGRAM_CONTEST', object))

        if category == 'PROBLEM':
            result.append(plot_kind('POINTS_HISTOGRAM_PROBLEM', object))
            result.append(plot_kind('POINTS_TABLE_PROBLEM', object))
            result.append(plot_kind('POINTS_TO_SOURCE_LENGTH_PROBLEM', object))
            result.append(plot_kind('TEST_SCORES_TABLE_PROBLEM', object))

        return result

    def statistics_data(self, request, plot_kind, object):
        (plot_function, plot_type) = plot_kind
        result = plot_function(request, object)
        result['plot_type'] = plot_type

        return result

    def render_statistics(self, request, data, plot_id):
        return data['plot_type'].render_plot(request, data, plot_id)

ProgrammingContestController.mix_in(
    StatisticsMixinForProgrammingContestController)
Пример #13
0
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _

from oioioi.programs.controllers import ProgrammingContestController


class SubmitServiceMixinForProgrammingContestController(object):
    """ContestController mixin that adds information about the possibility to
       submit solutions from terminal.
    """

    def adjust_submission_form(self, request, form, problem_instance):
        super(SubmitServiceMixinForProgrammingContestController, self) \
            .adjust_submission_form(request, form, problem_instance)
        form.fields['file'].help_text = \
            mark_safe(form.fields['file'].help_text + _(
                " Alternatively, you can "
                "<a href='%s'>submit your solutions from terminal</a>."
            ) % reverse('submitservice_view_user_token',
                        kwargs={'contest_id': request.contest.id}))

ProgrammingContestController.mix_in(
    SubmitServiceMixinForProgrammingContestController)
Пример #14
0
from oioioi.evalmgr import add_before_placeholder, add_after_recipe_entry
from oioioi.programs.controllers import ProgrammingContestController


class SuspendJudgeContestControllerMixin(object):
    def finalize_evaluation_environment(self, environ):
        super(SuspendJudgeContestControllerMixin, self) \
                .finalize_evaluation_environment(environ)

        add_after_recipe_entry(environ, 'mark_submission_in_progress', (
            'check_problem_instance_state',
            'oioioi.suspendjudge.handlers.check_problem_instance_state',
            dict(suspend_init_tests=True)))

        try:
            add_before_placeholder(environ, 'before_final_tests', (
                'check_problem_instance_state',
                'oioioi.suspendjudge.handlers.check_problem_instance_state'))
        except IndexError:
            pass


ProgrammingContestController.mix_in(SuspendJudgeContestControllerMixin)
Пример #15
0
class CodeSharingContestControllerMixin(object):
    """A mixin allowing users to see shared submissions' contents.
    """
    def filter_visible_sources(self, request, queryset):
        prev = super(CodeSharingContestControllerMixin, self) \
                .filter_visible_sources(request, queryset)

        if not request.user.is_authenticated():
            return prev

        shared = CodeSharingController().all_shared_with_me(request.user)
        return prev | queryset.filter(id__in=shared)


ProgrammingContestController.mix_in(CodeSharingContestControllerMixin)


class ProblemExperienceMixinForContestController(object):
    """A helper mixin for oioioi.gamification.ProblemExperienceSource."""
    def submission_judged(self, submission, rejudged=False):
        super(ProblemExperienceMixinForContestController, self)\
                .submission_judged(submission, rejudged)

        try:
            report = SubmissionReport.objects.get(
                userresultforproblem__user=submission.user,
                userresultforproblem__problem_instance=
                    submission.problem_instance
            )
Пример #16
0
            q_expression = Q(submissions__submission=submission)
        else:
            # Do not split this filter as it spans many-to-many relationship
            q_expression = Q(submissions__submission=submission,
                             submissions__guilty=True)
        similarities = SubmissionsSimilarityGroup.objects \
            .filter(q_expression) \
            .select_related('submissions')
        if not similarities:
            return prev

        submission_contexts = {}
        for group in similarities:
            for entry in group.submissions.all():
                submission_contexts[entry.submission] = \
                        submission_template_context(request, entry.submission)

        template = ('similarsubmits/programming_similar_submissions_admin.html'
                    if is_contest_admin(request) else
                    'similarsubmits/programming_similar_submissions.html')

        context = RequestContext(request, {
            'similarities': similarities,
            'main_submission_id': submission.id,
            'submission_contexts': submission_contexts,
        })

        return prev + render_to_string(template, context_instance=context)

ProgrammingContestController.mix_in(SimilarityDisqualificationMixin)
Пример #17
0
from oioioi.confirmations.utils import send_submission_receipt_confirmation
from oioioi.programs.controllers import ProgrammingContestController


class ConfirmationContestControllerMixin(object):
    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)

        if self.should_confirm_submission_receipt(request, submission):
            send_submission_receipt_confirmation(request, submission)
        return submission


ProgrammingContestController.mix_in(ConfirmationContestControllerMixin)
Пример #18
0
from oioioi.confirmations.utils import send_submission_receipt_confirmation
from oioioi.programs.controllers import ProgrammingContestController


class ConfirmationContestControllerMixin(object):
    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)

        if self.should_confirm_submission_receipt(request, submission):
            send_submission_receipt_confirmation(request, submission)
        return submission


ProgrammingContestController.mix_in(ConfirmationContestControllerMixin)
Пример #19
0
class OiSubmitContestControllerMixin(object):
    """ContestController mixin that adds extra information about submission
       from the oisubmit app to the submission footer.
    """

    def render_submission_footer(self, request, submission):
        super_footer = super(OiSubmitContestControllerMixin, self). \
                render_submission_footer(request, submission)

        if not hasattr(submission, 'oisubmitextradata') or \
               submission.oisubmitextradata is None or \
               not is_contest_admin(request):
            return super_footer

        def _get_extra(s):
            return getattr(submission.oisubmitextradata, s, '')

        return render_to_string('oisubmit/submission-footer.html',
                request=request,
                context={
                    'received_suspected': _get_extra('received_suspected'),
                    'comments': _get_extra('comments'),
                    'localtime': _get_extra('localtime'),
                    'siotime': _get_extra('siotime'),
                    'servertime': _get_extra('servertime'),
                }) + super_footer


ProgrammingContestController.mix_in(OiSubmitContestControllerMixin)
Пример #20
0
    """

    def get_prizes_distributors(self):
        """Returns a dictionary of functions distributing prizes.

           Each funtion takes a PrizeGiving object as the sole argument
           and distributes Prizes to Users within this PrizeGiving through
           creation of appropriate PrizeForUser objects.
           Function may raise AssignmentNotFound. If that's the case,
           all created PrizeForUser objects are deleted and distribution
           fails.
        """
        return {'total_score': (_("total score"), by_result_for_contest)}

    def get_prizes_email_addresses(self, pg):
        """Returns a list of email addresses to which a message will
           be sent, informing about the distribution failure of ``pg``.

           Defaults to empty list.
        """
        return []

    def can_see_prizes(self, request):
        """Determines if the current user is allowed to see awarded prizes.

           By default everybody has access.
        """
        return True

ProgrammingContestController.mix_in(PrizesControllerMixin)
        super(SubmitsQueueContestControllerMixin, self).\
            finalize_evaluation_environment(environ)
        environ['recipe'].insert(
            0, ('mark_submission_in_progress',
                'oioioi.submitsqueue.handlers.mark_submission_in_progress'))
        if 'postpone_handlers' not in environ:
            environ['postpone_handlers'] = []
        environ['postpone_handlers'].append(
            ('update_celery_task_id',
             'oioioi.submitsqueue.handlers.update_celery_task_id'))
        if 'error_handlers' not in environ:
            environ['error_handlers'] = []
        environ['error_handlers'].insert(
            0, ('remove_submission_on_error',
                'oioioi.submitsqueue.handlers.remove_submission_on_error'))

    def submission_queued(self, submission, async_result):
        super(SubmitsQueueContestControllerMixin, self).\
            submission_queued(submission, async_result)
        QueuedSubmit.objects.get_or_create(submission=submission,
                                           celery_task_id=async_result.id)

    def submission_unqueued(self, submission, job_id):
        super(SubmitsQueueContestControllerMixin, self).\
            submission_unqueued(submission, job_id)
        QueuedSubmit.objects.filter(submission=submission,
                                    celery_task_id=job_id).delete()


ProgrammingContestController.mix_in(SubmitsQueueContestControllerMixin)
Пример #22
0
            pis = visible_problem_instances(request)

            # Do not add this plot if it would be empty.
            if any(
                    pi_results_visible(pi) and not pi.round.is_trial
                    for pi in pis):
                result.append(plot_kind('POINTS_HISTOGRAM_CONTEST', object))

        if category == 'PROBLEM':
            result.append(plot_kind('POINTS_HISTOGRAM_PROBLEM', object))
            result.append(plot_kind('POINTS_TABLE_PROBLEM', object))
            result.append(plot_kind('POINTS_TO_SOURCE_LENGTH_PROBLEM', object))
            result.append(plot_kind('TEST_SCORES_TABLE_PROBLEM', object))

        return result

    def statistics_data(self, request, plot_kind, object):
        (plot_function, plot_type) = plot_kind
        result = plot_function(request, object)
        result['plot_type'] = plot_type

        return result

    def render_statistics(self, request, data, plot_id):
        return data['plot_type'].render_plot(request, data, plot_id)


ProgrammingContestController.mix_in(
    StatisticsMixinForProgrammingContestController)
Пример #23
0
class PrizesControllerMixin(object):
    def get_prizes_distributors(self):
        """Returns a dictionary of functions distributing prizes.

           Each funtion takes a PrizeGiving object as the sole argument
           and distributes Prizes to Users within this PrizeGiving through
           creation of appropriate PrizeForUser objects.
           Function may raise AssignmentNotFound. If that's the case,
           all created PrizeForUser objects are deleted and distribution
           fails.
        """
        return {'total_score': (_("total score"), by_result_for_contest)}

    def get_prizes_email_addresses(self, pg):
        """Returns a list of email addresses to which a message will
           be sent, informing about the distribution failure of ``pg``.

           Defaults to empty list.
        """
        return []

    def can_see_prizes(self, request):
        """Determines if the current user is allowed to see awarded prizes.

           By default everybody has access.
        """
        return True

ProgrammingContestController.mix_in(PrizesControllerMixin)
Пример #24
0
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _

from oioioi.programs.controllers import ProgrammingContestController


class SubmitServiceMixinForProgrammingContestController(object):
    def adjust_submission_form(self, request, form, problem_instance):
        super(SubmitServiceMixinForProgrammingContestController, self) \
            .adjust_submission_form(request, form, problem_instance)
        form.fields['file'].help_text = \
            mark_safe(form.fields['file'].help_text + _(
                " Alternatively, you can "
                "<a href='%s'>submit your solutions from terminal</a>."
            ) % reverse('submitservice_view_user_token',
                        kwargs={'contest_id': request.contest.id}))


ProgrammingContestController.mix_in(
    SubmitServiceMixinForProgrammingContestController)
Пример #25
0
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User

from oioioi.base.tests import check_not_accessible
from oioioi.contests.models import Contest, ProblemInstance
from oioioi.programs.controllers import ProgrammingContestController
from oioioi.questions.models import Message, ReplyTemplate
from oioioi.base.notification import NotificationHandler


class TestContestControllerMixin(object):
    def users_to_receive_public_message_notification(self):
        return self.registration_controller().filter_participants(User
                .objects.all())

ProgrammingContestController.mix_in(TestContestControllerMixin)


class TestQuestions(TestCase):
    fixtures = ['test_users', 'test_contest', 'test_full_package',
                'test_problem_instance', 'test_messages', 'test_templates']

    def test_visibility(self):
        contest = Contest.objects.get()
        all_messages = ['problem-question', 'contest-question',
                'public-answer', 'private-answer']
        url = reverse('contest_messages', kwargs={'contest_id': contest.id})

        def check_visibility(*should_be_visible):
            response = self.client.get(url)
            for m in all_messages:
Пример #26
0
    def finalize_evaluation_environment(self, environ):
        super(SuspendJudgeContestControllerMixin,
              self).finalize_evaluation_environment(environ)
        try:
            add_before_recipe_entry(
                environ,
                'compile',
                (
                    'check_problem_instance_state',
                    'oioioi.suspendjudge.handlers.check_problem_instance_state',
                    dict(suspend_init_tests=True),
                ),
            )
        except IndexError:
            pass

        try:
            add_before_placeholder(
                environ,
                'before_final_tests',
                (
                    'check_problem_instance_state',
                    'oioioi.suspendjudge.handlers.check_problem_instance_state',
                ),
            )
        except IndexError:
            pass


ProgrammingContestController.mix_in(SuspendJudgeContestControllerMixin)
Пример #27
0
class DisqualificationProgrammingContestControllerMixin(object):
    """ContestController mixin that renders submission disqualification info."""
    def render_submission(self, request, submission):
        prev = super(DisqualificationProgrammingContestControllerMixin,
                     self).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)
        return prev


ProgrammingContestController.mix_in(
    DisqualificationProgrammingContestControllerMixin)


class WithDisqualificationRankingControllerMixin(object):
    """RankingController mixin that manages disqualification module influence
    on rankings.
    """
    def _show_disqualified(self, key):
        """Decides if disqualified users should be included in the ranking.

        They will be marked as disqualified and will *not* influence the
        places of other contestants.
        """
        return self.is_admin_key(key)

    def filter_users_for_ranking(self, key, queryset):
Пример #28
0
from django.template.context import RequestContext
from django.template.loader import render_to_string
from oioioi.contests.utils import is_contest_admin
from oioioi.programs.controllers import ProgrammingContestController


class OiSubmitContestControllerMixin(object):
    def render_submission_footer(self, request, submission):
        super_footer = super(OiSubmitContestControllerMixin, self). \
                render_submission_footer(request, submission)

        if not hasattr(submission, 'oisubmitextradata') or \
               submission.oisubmitextradata is None or \
               not is_contest_admin(request):
            return super_footer

        def _get_extra(s):
            return getattr(submission.oisubmitextradata, s, '')

        return render_to_string('oisubmit/submission_footer.html',
            context_instance=RequestContext(request, {
                'received_suspected': _get_extra('received_suspected'),
                'comments': _get_extra('comments'),
                'localtime': _get_extra('localtime'),
                'siotime': _get_extra('siotime'),
                'servertime': _get_extra('servertime'),
            })) + super_footer

ProgrammingContestController.mix_in(OiSubmitContestControllerMixin)