Newer
Older
This page type allows both automatic grading and grading
by a human grader.
:attr:`course.constants.flow_permission.change_answer`
permission for your entire flow, you likely want to
include this snippet in your question definition:
.. code-block:: yaml
access_rules:
add_permissions:
- change_answer
This will allow participants multiple attempts at getting
the right answer.
Besides those defined in :class:`PythonCodeQuestion`, the
following additional, allowed/required attribute are introduced:
.. attribute:: human_feedback_value
A number. The point value of the feedback component
by the human grader (who will grade on a 0-100 scale,
which is scaled to yield :attr:`human_feedback_value`
at 100).
.. attribute:: human_feedback_percentage
Optional.
A number. The percentage the feedback by the human
grader takes in the overall grade. Noticing that
either this attribute or :attr:`human_feedback_value`
must be included. `
.. attribute:: rubric
Required.
The grading guideline for this question (for the human-graded component
of the question), in :ref:`markup`.
"""
def __init__(self, vctx, location, page_desc):
super(PythonCodeQuestionWithHumanTextFeedback, self).__init__(
vctx, location, page_desc)
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
if vctx is not None:
if (hasattr(self.page_desc, "human_feedback_value")
and
hasattr(self.page_desc, "human_feedback_percentage")):
raise ValidationError(
string_concat(
"%(location)s: ",
_("'human_feedback_value' and "
"'human_feedback_percentage' are not "
"allowed to coexist"))
% {'location': location}
)
if not (hasattr(self.page_desc, "human_feedback_value")
or hasattr(self.page_desc, "human_feedback_percentage")):
raise ValidationError(
string_concat(
"%(location)s: ",
_("expecting either 'human_feedback_value' "
"or 'human_feedback_percentage', found neither."))
% {'location': location}
)
if hasattr(self.page_desc, "human_feedback_value"):
vctx.add_warning(
location,
_("Used deprecated 'human_feedback_value' attribute--"
"use 'human_feedback_percentage' instead."))
if self.page_desc.value == 0:
raise ValidationError("".join([
"%s: ",
_("'human_feedback_value' attribute is not allowed "
"if value of question is 0, use "
"'human_feedback_percentage' instead")])
% location)
if self.page_desc.human_feedback_value > self.page_desc.value:
raise ValidationError("".join([
"%s: ",
_("human_feedback_value greater than overall "
"value of question")])
% location)
if hasattr(self.page_desc, "human_feedback_percentage"):
if not (
0 <= self.page_desc.human_feedback_percentage <= 100):
raise ValidationError("".join([
"%s: ",
_("the value of human_feedback_percentage "
"must be between 0 and 100")])
% location)
if hasattr(self.page_desc, "human_feedback_value"):
self.human_feedback_percentage = (
self.page_desc.human_feedback_value * 100 / self.page_desc.value)
self.human_feedback_percentage = (
self.page_desc.human_feedback_percentage)
def required_attrs(self):
return super(
PythonCodeQuestionWithHumanTextFeedback, self).required_attrs() + (
# value is otherwise optional, but we require it here
("value", (int, float)),
)
def allowed_attrs(self):
return super(
PythonCodeQuestionWithHumanTextFeedback, self).allowed_attrs() + (
("human_feedback_value", (int, float)),
def human_feedback_point_value(self, page_context, page_data):
return self.page_desc.value * self.human_feedback_percentage / 100
def grade(self, page_context, page_data, answer_data, grade_data):
if answer_data is None:
return AnswerFeedback(correctness=0,
if grade_data is not None and not grade_data["released"]:
grade_data = None
code_feedback = PythonCodeQuestion.grade(self, page_context,
page_data, answer_data, grade_data)
human_points = self.human_feedback_point_value(page_context, page_data)
code_points = self.page_desc.value - human_points
correctness = None
percentage = None
if (code_feedback is not None
and code_feedback.correctness is not None
and grade_data is not None
and grade_data["grade_percent"] is not None):
code_feedback_percentage = 100 - self.human_feedback_percentage
percentage = (
code_feedback.correctness * code_feedback_percentage
+ grade_data["grade_percent"] / 100
* self.human_feedback_percentage
)
correctness = percentage / 100
elif (self.human_feedback_percentage == 100
and grade_data is not None
and grade_data["grade_percent"] is not None):
correctness = grade_data["grade_percent"] / 100
percentage = correctness * 100
elif (self.human_feedback_percentage == 0
and code_feedback.correctness is not None):
correctness = code_feedback.correctness
percentage = correctness * 100
human_feedback_text = None
human_feedback_points = None
assert grade_data["feedback_text"] is not None
if grade_data["feedback_text"].strip():
human_feedback_text = markup_to_html(
page_context, grade_data["feedback_text"])
human_graded_percentage = grade_data["grade_percent"]
if human_graded_percentage is not None:
human_feedback_points = (human_graded_percentage/100.
* human_points)
code_feedback_points = None
if (code_feedback is not None
and code_feedback.correctness is not None):
code_feedback_points = code_feedback.correctness*code_points
from django.template.loader import render_to_string
feedback = render_to_string(
"course/feedback-code-with-human.html",
{
"percentage": percentage,
"code_feedback": code_feedback,
"code_feedback_points": code_feedback_points,
"code_points": code_points,
"human_feedback_text": human_feedback_text,
"human_feedback_points": human_feedback_points,
"human_points": human_points,
})
return AnswerFeedback(
correctness=correctness,
feedback=feedback,
bulk_feedback=code_feedback.bulk_feedback)
# vim: foldmethod=marker