Skip to content

Commit bda844c

Browse files
tuncbkoseGehock
authored andcommitted
Skip duplicated grade_ids during autograding
1 parent 5cb3e59 commit bda844c

File tree

7 files changed

+64
-4
lines changed

7 files changed

+64
-4
lines changed

nbgrader/converters/autograde.py

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from ..preprocessors import (
1111
AssignLatePenalties, ClearOutput, DeduplicateIds, OverwriteCells, SaveAutoGrades,
1212
Execute, LimitOutput, OverwriteKernelspec, CheckCellMetadata, IgnorePattern)
13+
from ..postprocessors import CheckDuplicateFlag
1314
from ..api import Gradebook, MissingEntry
1415
from .. import utils
1516

@@ -196,3 +197,6 @@ def convert_single_notebook(self, notebook_filename: str) -> None:
196197
super(Autograde, self).convert_single_notebook(notebook_filename)
197198
finally:
198199
self._sanitizing = True
200+
201+
self.log.info(f"Post-processing {notebook_filename}")
202+
CheckDuplicateFlag(notebook_filename)

nbgrader/converters/base.py

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from ..coursedir import CourseDirectory
1818
from ..utils import find_all_files, rmtree, remove
1919
from ..preprocessors.execute import UnresponsiveKernelError
20+
from ..postprocessors import DuplicateIdError
2021
from ..nbgraderformat import SchemaTooOldError, SchemaTooNewError
2122
import typing
2223
from nbconvert.exporters.exporter import ResourcesDict
@@ -405,6 +406,13 @@ def _handle_failure(gd: typing.Dict[str, str]) -> None:
405406
errors.append((gd['assignment_id'], gd['student_id']))
406407
_handle_failure(gd)
407408

409+
except DuplicateIdError:
410+
self.log.error(
411+
f"Encountered a cell with duplicate id when processing {notebook_filename}. "
412+
"Autograding with skipping cells marked as duplicate."
413+
)
414+
errors.append((gd['assignment_id'], gd['student_id']))
415+
408416
# Raise unhandled exceptions for the outer try/except
409417
except Exception as e:
410418
raise e

nbgrader/nbgraderformat/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@
44
from .v4 import reads_v4 as reads, writes_v4 as writes
55

66
SCHEMA_VERSION = MetadataValidator.schema_version
7+
8+
# Metadata required by latest schema, along with default values
9+
SCHEMA_REQUIRED = {"schema_version": 4,
10+
"grade": False,
11+
"locked": False,
12+
"solution": False}

nbgrader/postprocessors/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .checkduplicateflag import CheckDuplicateFlag, DuplicateIdError
2+
3+
__all__ = [
4+
"CheckDuplicateFlag",
5+
"DuplicateIdError"
6+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import nbformat
2+
from nbformat.notebooknode import NotebookNode
3+
4+
5+
class DuplicateIdError(Exception):
6+
7+
def __init__(self, message):
8+
super(DuplicateIdError, self).__init__(message)
9+
10+
11+
class CheckDuplicateFlag:
12+
13+
def __init__(self, notebook_filename):
14+
with open(notebook_filename, encoding="utf-8") as f:
15+
nb = nbformat.read(f, as_version=nbformat.NO_CONVERT)
16+
self.postprocess(nb)
17+
18+
def postprocess(self, nb: NotebookNode):
19+
for cell in nb.cells:
20+
self.postprocess_cell(cell)
21+
22+
@staticmethod
23+
def postprocess_cell(cell: NotebookNode):
24+
if "nbgrader" in cell.metadata and "duplicate" in cell.metadata.nbgrader:
25+
del cell.metadata.nbgrader["duplicate"]
26+
msg = "Detected cells with same ids"
27+
raise DuplicateIdError(msg)

nbgrader/preprocessors/deduplicateids.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .. import utils
22
from . import NbGraderPreprocessor
3+
from ..nbgraderformat import SCHEMA_REQUIRED
34
from nbconvert.exporters.exporter import ResourcesDict
45
from nbformat.notebooknode import NotebookNode
56
from typing import Tuple
@@ -27,7 +28,10 @@ def preprocess_cell(self,
2728
grade_id = cell.metadata.nbgrader['grade_id']
2829
if grade_id in self.grade_ids:
2930
self.log.warning("Cell with id '%s' exists multiple times!", grade_id)
30-
cell.metadata.nbgrader = {}
31+
# Replace problematic metadata and leave message
32+
cell.source = "# THIS CELL CONTAINED A DUPLICATE ID DURING AUTOGRADING\n" + cell.source
33+
cell.metadata.nbgrader = SCHEMA_REQUIRED #| {"duplicate": True} # doesn't work in python 3.8
34+
cell.metadata.nbgrader["duplicate"] = True
3135
else:
3236
self.grade_ids.add(grade_id)
3337

nbgrader/tests/preprocessors/test_deduplicateids.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from nbformat.v4 import new_notebook
44

55
from ...preprocessors import DeduplicateIds
6+
from ...nbgraderformat import SCHEMA_REQUIRED
67
from .base import BaseTestPreprocessor
78
from .. import (
89
create_grade_cell, create_solution_cell, create_locked_cell)
@@ -14,6 +15,10 @@ def preprocessor():
1415
return pp
1516

1617

18+
EXPECTED_DUPLICATE_METADATA = SCHEMA_REQUIRED # | {"duplicate": True} # doesn't work in python 3.8
19+
EXPECTED_DUPLICATE_METADATA["duplicate"] = True
20+
21+
1722
class TestDeduplicateIds(BaseTestPreprocessor):
1823

1924
def test_duplicate_grade_cell(self, preprocessor):
@@ -26,7 +31,7 @@ def test_duplicate_grade_cell(self, preprocessor):
2631
nb, resources = preprocessor.preprocess(nb, {})
2732

2833
assert nb.cells[0].metadata.nbgrader != {}
29-
assert nb.cells[1].metadata.nbgrader == {}
34+
assert nb.cells[1].metadata.nbgrader == EXPECTED_DUPLICATE_METADATA
3035

3136
def test_duplicate_solution_cell(self, preprocessor):
3237
cell1 = create_solution_cell("hello", "code", "foo")
@@ -38,7 +43,7 @@ def test_duplicate_solution_cell(self, preprocessor):
3843
nb, resources = preprocessor.preprocess(nb, {})
3944

4045
assert nb.cells[0].metadata.nbgrader != {}
41-
assert nb.cells[1].metadata.nbgrader == {}
46+
assert nb.cells[1].metadata.nbgrader == EXPECTED_DUPLICATE_METADATA
4247

4348
def test_duplicate_locked_cell(self, preprocessor):
4449
cell1 = create_locked_cell("hello", "code", "foo")
@@ -50,4 +55,4 @@ def test_duplicate_locked_cell(self, preprocessor):
5055
nb, resources = preprocessor.preprocess(nb, {})
5156

5257
assert nb.cells[0].metadata.nbgrader != {}
53-
assert nb.cells[1].metadata.nbgrader == {}
58+
assert nb.cells[1].metadata.nbgrader == EXPECTED_DUPLICATE_METADATA

0 commit comments

Comments
 (0)