from django import forms
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
from django.db import transaction
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.html import format_html, format_html_join
from django.utils.translation import gettext_lazy as _
from oioioi.contests.controllers import submission_template_context
from oioioi.contests.models import ScoreReport, Submission, SubmissionReport
from oioioi.contests.utils import is_contest_basicadmin
from oioioi.problems.controllers import ProblemController
from oioioi.problems.utils import can_admin_problem_instance
from oioioi.programs.controllers import ContestController
from oioioi.quizzes.models import (
QuestionReport,
QuizAnswer,
QuizSubmission,
QuizSubmissionAnswer,
QuizSubmissionTextAnswer,
)
from oioioi.quizzes.utils import quizbleach
[docs]class QuizContestControllerMixin(object):
"""ContestController mixin that sets up quiz config for the contest."""
[docs] def is_quiz_question_answer_case_ignored(self, question):
"""Determines if checking a quiz questions' answer
should be case insensitive.
"""
return True
ContestController.mix_in(QuizContestControllerMixin)
[docs]class QuizProblemController(ProblemController):
"""Defines rules for quizzes."""
[docs] def adjust_problem(self):
"""Called whan a (usually new) problem has just got the controller
attached or after the problem has been modified.
"""
pass
[docs] def render_pictures(self, pictures):
return format_html(
u'''<div class="quiz_pictures_container">
<table>
<tr>{}</tr>
</table>
</div>''',
format_html_join(
u'\n',
u'''<td class="text-center">
<img src="{}" class="quiz_picture" /><br>
<span class="quiz_caption"> {}</span>
</td>''',
((p.get_absolute_url(), p.caption) for p in pictures),
),
) if pictures else u''
[docs] def render_question(self, request, question):
pictures = question.quizquestionpicture_set.all()
return format_html(
u'''
<strong>{}</strong>
{}
''',
quizbleach(question.question),
self.render_pictures(pictures),
)
[docs] def render_answer(self, request, answer):
pictures = answer.quizanswerpicture_set.all()
return format_html(
u'''
{}
{}
''',
quizbleach(answer.answer),
self.render_pictures(pictures),
)
[docs] def select_questions(self, user, problem_instance, submission):
return problem_instance.problem.quiz.quizquestion_set.all()
[docs] def create_submission(self, request, problem_instance, form_data, **kwargs):
judge_after_create = kwargs.get('judge_after_create', True)
with transaction.atomic():
questions = self.select_questions(
form_data.get('user', request.user), problem_instance, None
)
submission = QuizSubmission(
user=form_data.get('user', request.user),
problem_instance=problem_instance,
kind=form_data.get(
'kind',
problem_instance.controller.get_default_submission_kind(
request, problem_instance=problem_instance
),
),
date=request.timestamp,
)
submission.save()
# add answers to submission
for question in questions:
field_id = self._form_field_id_for_question(problem_instance, question)
selected_answers = self._get_selected_answers(
form_data, field_id, question
)
self._submit_answers(selected_answers, question, submission)
if judge_after_create:
problem_instance.controller.judge(submission)
return submission
[docs] def _submit_answers(self, selected_answers, question, submission):
if question.is_text_input:
sub = QuizSubmissionTextAnswer.objects.create(
quiz_submission=submission,
question=question,
text_answer=selected_answers[0],
)
sub.save()
else:
answers = {
a.id: a.id in selected_answers for a in question.quizanswer_set.all()
}
for aid, selected in answers.items():
answer = QuizAnswer.objects.get(id=aid)
sub = QuizSubmissionAnswer.objects.create(
quiz_submission=submission, answer=answer, is_selected=selected
)
sub.save()
[docs] def _get_selected_answers(self, form_data, field_id, question):
field_value = form_data.get(field_id)
if question.is_text_input:
if isinstance(field_value, list):
return field_value[:1]
return [field_value]
elif question.is_multiple_choice:
return [int(a) for a in field_value]
else:
return [int(field_value)]
[docs] def is_quiz_question_answer_case_ignored(self, question):
return True
[docs] def mixins_for_admin(self):
from oioioi.quizzes.admin import QuizAdminMixin
return super(QuizProblemController, self).mixins_for_admin() + (QuizAdminMixin,)
[docs] def render_report_failure(self, request, report):
return ProblemController.render_report(self, request, report)
[docs] def render_report(self, request, report):
problem_instance = report.submission.problem_instance
if report.kind == 'FAILURE':
return problem_instance.controller.render_report_failure(request, report)
score_report = ScoreReport.objects.get(submission_report=report)
picontroller = problem_instance.controller
question_reports = QuestionReport.objects.filter(
submission_report=report
).order_by('question__order')
return render_to_string(
'quizzes/report.html',
request=request,
context={
'report': report,
'score_report': score_report,
'question_reports': question_reports,
'is_admin': picontroller.is_admin(request, report),
},
)
[docs] def render_submission(self, request, submission):
problem_instance = submission.problem_instance
can_admin = can_admin_problem_instance(request, submission.problem_instance)
context = {
'submission': submission_template_context(
request, submission.quizsubmission
),
'supported_extra_args': problem_instance.controller.get_supported_extra_args(
submission
),
'can_admin': can_admin,
}
return render_to_string(
'quizzes/submission_header.html', request=request, context=context
)
[docs] def update_submission_score(self, submission):
try:
report = SubmissionReport.objects.filter(
submission=submission, status='ACTIVE', kind='NORMAL'
).get()
score_report = ScoreReport.objects.get(submission_report=report)
submission.status = score_report.status
submission.score = score_report.score
except SubmissionReport.DoesNotExist:
if SubmissionReport.objects.filter(
submission=submission, status='ACTIVE', kind='FAILURE'
):
submission.status = 'SE'
else:
submission.status = '?'
submission.score = None
submission.save()
[docs] def fill_evaluation_environ(self, environ, submission, **kwargs):
self.generate_base_environ(environ, submission, **kwargs)
[docs] def generate_base_environ(self, environ, submission, **kwargs):
self.generate_initial_evaluation_environ(environ, submission)
environ.setdefault('recipe', []).append(
('score_quiz', 'oioioi.quizzes.handlers.score_quiz'),
)
[docs] def generate_initial_evaluation_environ(self, environ, submission, **kwargs):
problem_instance = submission.problem_instance
problem = problem_instance.problem
contest = problem_instance.contest
if contest is not None:
round = problem_instance.round
environ['round_id'] = round.id
environ['contest_id'] = contest.id
environ['submission_id'] = submission.id
environ['submission_kind'] = submission.kind
environ['problem_instance_id'] = problem_instance.id
environ['problem_id'] = problem.id
environ['problem_short_name'] = problem.short_name
environ['submission_owner'] = (
submission.user.username if submission.user else None
)
environ['oioioi_instance'] = settings.SITE_NAME
environ['contest_priority'] = (
contest.judging_priority
if contest is not None
else settings.NON_CONTEST_PRIORITY
)
environ['contest_priority'] += settings.OIOIOI_INSTANCE_PRIORITY_BONUS
environ['contest_weight'] = (
contest.judging_weight
if contest is not None
else settings.NON_CONTEST_WEIGHT
)
environ['contest_weight'] += settings.OIOIOI_INSTANCE_WEIGHT_BONUS
environ.setdefault('report_kinds', ['NORMAL'])
if 'hidden_judge' in environ['extra_args']:
environ['report_kinds'] = ['HIDDEN']
[docs] def supports_problem_statement(self):
return False