Coverage for e2xgrader/graders/code.py: 95%
43 statements
« prev ^ index » next coverage.py v7.4.2, created at 2024-03-14 13:22 +0100
« prev ^ index » next coverage.py v7.4.2, created at 2024-03-14 13:22 +0100
1from logging import Logger
2from typing import Optional, Tuple
4from nbformat.notebooknode import NotebookNode
5from nbgrader.utils import get_partial_grade, is_solution
7from .base import BaseGrader
10class CodeGrader(BaseGrader):
11 def extract_grade(self, text, log: Logger = None):
12 delimiter_start = "### BEGIN GRADE"
13 delimiter_end = "### END GRADE"
14 inside = False
16 points = None
18 grade_lines = []
20 for line in text.split("\n"):
21 if line.startswith(delimiter_start):
22 inside = True
23 elif line.startswith(delimiter_end):
24 inside = False
25 break
26 elif inside:
27 grade_lines.append(line)
29 if len(grade_lines) > 0 and not inside:
30 # Try casting result to float
31 try:
32 points = float("".join(grade_lines))
33 except ValueError:
34 pass
35 return points
37 def determine_grade(
38 self, cell: NotebookNode, log: Logger = None
39 ) -> Tuple[Optional[float], float]:
40 max_points = float(cell.metadata["nbgrader"]["points"])
42 if not self.cell_changed(cell):
43 return 0, max_points
44 elif is_solution(cell):
45 return None, max_points
47 for output in cell.outputs:
48 # option 1: error, return 0
49 if (
50 output.output_type == "error"
51 or output.output_type == "stream"
52 and output.name == "stderr"
53 ):
54 return 0, max_points
55 if output.output_type == "execute_result":
56 partial_grade = get_partial_grade(output, max_points, log)
57 return partial_grade, max_points
58 if output.output_type == "stream" and output.name == "stdout":
59 partial_grade = self.extract_grade(output.text, log)
60 if partial_grade is not None:
61 return partial_grade, max_points
63 return max_points, max_points