from typing import Tuple
from nbconvert.exporters.exporter import ResourcesDict
from nbformat.notebooknode import NotebookNode
from grader_convert.gradebook.gradebook import Gradebook
from grader_convert import utils
from grader_convert.preprocessors.base import NbGraderPreprocessor
[docs]class SaveAutoGrades(NbGraderPreprocessor):
"""Preprocessor for saving out the autograder grades into a database"""
[docs] def preprocess(
self, nb: NotebookNode, resources: ResourcesDict
) -> Tuple[NotebookNode, ResourcesDict]:
# pull information from the resources
self.notebook_id = resources["unique_key"]
self.json_path = resources["output_json_path"]
self.gradebook = Gradebook(self.json_path)
with self.gradebook:
# process the cells
nb, resources = super(SaveAutoGrades, self).preprocess(nb, resources)
return nb, resources
def _add_score(self, cell: NotebookNode, resources: ResourcesDict) -> None:
"""Graders can override the autograder grades, and may need to
manually grade written solutions anyway. This function adds
score information to the database if it doesn't exist. It does
NOT override the 'score' field, as this is the manual score
that might have been provided by a grader.
"""
# these are the fields by which we will identify the score
# information
grade = self.gradebook.find_grade(
cell.metadata["nbgrader"]["grade_id"], self.notebook_id
)
# determine what the grade is
auto_score, _ = utils.determine_grade(cell, self.log)
grade.auto_score = auto_score
# if there was previously a manual grade, or if there is no autograder
# score, then we should mark this as needing review
if (grade.manual_score is not None) or (grade.auto_score is None):
grade.needs_manual_grade = True
else:
grade.needs_manual_grade = False
self.gradebook.add_grade(
cell.metadata["nbgrader"]["grade_id"], self.notebook_id, grade
)
def _add_comment(self, cell: NotebookNode, resources: ResourcesDict) -> None:
comment = self.gradebook.find_comment(
cell.metadata["nbgrader"]["grade_id"], self.notebook_id
)
if cell.metadata.nbgrader.get("checksum", None) == utils.compute_checksum(
cell
) and not utils.is_task(cell):
comment.auto_comment = "No response."
else:
comment.auto_comment = None
self.gradebook.add_comment(
cell.metadata["nbgrader"]["grade_id"], self.notebook_id, comment
)
[docs] def preprocess_cell(
self, cell: NotebookNode, resources: ResourcesDict, cell_index: int
) -> Tuple[NotebookNode, ResourcesDict]:
# if it's a grade cell, the add a grade
if utils.is_grade(cell):
self._add_score(cell, resources)
if utils.is_solution(cell):
self._add_comment(cell, resources)
if utils.is_task(cell):
self._add_comment(cell, resources)
return cell, resources