Skip to content
code.py 42.8 KiB
Newer Older
Andreas Klöckner's avatar
Andreas Klöckner committed

    This will allow participants multiple attempts at getting
    the right answer.

Dong Zhuang's avatar
Dong Zhuang committed
    Besides those defined in :class:`PythonCodeQuestion`, the
    following additional, allowed/required attribute are introduced:

    .. attribute:: human_feedback_value

Dong Zhuang's avatar
Dong Zhuang committed
        Optional (deprecated).
        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).

Dong Zhuang's avatar
Dong Zhuang committed
    .. 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)

Dong Zhuang's avatar
Dong Zhuang committed
        if vctx is not None:
Andreas Klöckner's avatar
Andreas Klöckner committed
            if (
                    hasattr(self.page_desc, "human_feedback_value")
                    and hasattr(self.page_desc, "human_feedback_percentage")):
Dong Zhuang's avatar
Dong Zhuang committed
                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)
Dong Zhuang's avatar
Dong Zhuang committed
        else:
            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)),
Dong Zhuang's avatar
Dong Zhuang committed
                        )

    def allowed_attrs(self):
        return super(
                PythonCodeQuestionWithHumanTextFeedback, self).allowed_attrs() + (
                        ("human_feedback_value", (int, float)),
Dong Zhuang's avatar
Dong Zhuang committed
                        ("human_feedback_percentage", (int, float)),
    def human_feedback_point_value(self, page_context, page_data):
Dong Zhuang's avatar
Dong Zhuang committed
        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,
ifaint's avatar
ifaint committed
                    feedback=_("No answer provided."))

        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)

Dong Zhuang's avatar
Dong Zhuang committed
        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):
Dong Zhuang's avatar
Dong Zhuang committed
            code_feedback_percentage = 100 - self.human_feedback_percentage
            percentage = (
                    code_feedback.correctness * code_feedback_percentage

                    + grade_data["grade_percent"] / 100
Dong Zhuang's avatar
Dong Zhuang committed
                    * 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
        if grade_data is not 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"])

Dong Zhuang's avatar
Dong Zhuang committed
            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)