Coverage for e2xgrader/preprocessors/saveautogrades.py: 38%

53 statements  

« prev     ^ index     » next       coverage.py v7.4.2, created at 2024-03-14 13:22 +0100

1from textwrap import dedent 

2from typing import Tuple 

3 

4from e2xcore.utils.e2xgrader_cells import is_extra_cell 

5from e2xcore.utils.nbgrader_cells import grade_id 

6from nbconvert.exporters.exporter import ResourcesDict 

7from nbformat.notebooknode import NotebookNode 

8from nbgrader.api import Gradebook 

9from nbgrader.preprocessors import SaveAutoGrades as NbgraderSaveAutoGrades 

10from nbgrader.utils import determine_grade 

11from traitlets import Dict, Instance, List, TraitError, Unicode, validate 

12 

13from ..graders import BaseGrader, CodeGrader, MultipleChoiceGrader, SingleChoiceGrader 

14 

15 

16class SaveAutoGrades(NbgraderSaveAutoGrades): 

17 graders = Dict( 

18 key_trait=Unicode(), 

19 value_trait=Instance(klass=BaseGrader), 

20 default_value={ 

21 "code": CodeGrader(), 

22 "singlechoice": SingleChoiceGrader(), 

23 "multiplechoice": MultipleChoiceGrader(), 

24 }, 

25 ).tag(config=True) 

26 

27 cells = List( 

28 [], 

29 help=dedent( 

30 """ 

31 List of cells to save the autogrades for. If this is empty all cells will be saved. 

32 """ 

33 ), 

34 ).tag(config=True) 

35 

36 @validate("cells") 

37 def _validate_cells(self, proposal): 

38 value = proposal["value"] 

39 if len(value) == 1: 

40 elem = value[0].strip() 

41 if elem.startswith("[") and elem.endswith("]"): 

42 elem = elem[1:-1] 

43 value = [v.strip() for v in elem.split(",")] 

44 if not isinstance(value, list): 

45 raise TraitError("cells must be a list") 

46 return value 

47 

48 def cell_type(self, cell: NotebookNode): 

49 if is_extra_cell(cell): 

50 return cell.metadata.extended_cell.type 

51 return cell.cell_type 

52 

53 def _add_score(self, cell: NotebookNode, resources: ResourcesDict) -> None: 

54 """Graders can override the autograder grades, and may need to 

55 manually grade written solutions anyway. This function adds 

56 score information to the database if it doesn't exist. It does 

57 NOT override the 'score' field, as this is the manual score 

58 that might have been provided by a grader. 

59 

60 """ 

61 # these are the fields by which we will identify the score 

62 # information 

63 grade = self.gradebook.find_grade( 

64 cell.metadata["nbgrader"]["grade_id"], 

65 self.notebook_id, 

66 self.assignment_id, 

67 self.student_id, 

68 ) 

69 

70 # determine what the grade is 

71 if self.cell_type(cell) in self.graders: 

72 auto_score, _ = self.graders[self.cell_type(cell)].determine_grade( 

73 cell, self.log 

74 ) 

75 grade.auto_score = auto_score 

76 else: 

77 auto_score, _ = determine_grade(cell, self.log) 

78 grade.auto_score = auto_score 

79 

80 # if there was previously a manual grade, or if there is no autograder 

81 # score, then we should mark this as needing review 

82 if (grade.manual_score is not None) or (grade.auto_score is None): 

83 grade.needs_manual_grade = True 

84 else: 

85 grade.needs_manual_grade = False 

86 

87 self.gradebook.db.commit() 

88 

89 def preprocess( 

90 self, nb: NotebookNode, resources: ResourcesDict 

91 ) -> Tuple[NotebookNode, ResourcesDict]: 

92 # pull information from the resources 

93 self.notebook_id = resources["nbgrader"]["notebook"] 

94 self.assignment_id = resources["nbgrader"]["assignment"] 

95 self.student_id = resources["nbgrader"]["student"] 

96 self.db_url = resources["nbgrader"]["db_url"] 

97 

98 # connect to the database 

99 self.gradebook = Gradebook(self.db_url) 

100 

101 with self.gradebook: 

102 # process the cells 

103 nb, resources = super(SaveAutoGrades, self).preprocess(nb, resources) 

104 

105 return nb, resources 

106 

107 def preprocess_cell( 

108 self, cell: NotebookNode, resources: ResourcesDict, cell_index: int 

109 ) -> Tuple[NotebookNode, ResourcesDict]: 

110 if len(self.cells) == 0 or grade_id(cell) in self.cells: 

111 return super().preprocess_cell(cell, resources, cell_index) 

112 return cell, resources