class OverwriteGradeCells(Preprocessor): """A preprocessor to save information about grade cells.""" db_name = Unicode("gradebook", config=True, help="Database name") db_ip = Unicode("localhost", config=True, help="IP address for the database") db_port = Integer(27017, config=True, help="Port for the database") assignment_id = Unicode(u'assignment', config=True, help="Assignment ID") def preprocess(self, nb, resources): # connect to the mongo database self.gradebook = Gradebook(self.db_name, ip=self.db_ip, port=self.db_port) self.assignment = self.gradebook.find_assignment( assignment_id=self.assignment_id) self.notebook_id = resources['unique_key'] nb, resources = super(OverwriteGradeCells, self).preprocess(nb, resources) return nb, resources def preprocess_cell(self, cell, resources, cell_index): if utils.is_grade(cell): try: grade_cell = self.gradebook.find_grade_cell( grade_id=cell.metadata.nbgrader.grade_id, notebook_id=self.notebook_id, assignment=self.assignment) except: return cell, resources cell.metadata.nbgrader['points'] = grade_cell.max_score # we only want the source and checksum for non-solution cells if not utils.is_solution(cell) and grade_cell.source: old_checksum = grade_cell.checksum new_checksum = utils.compute_checksum(cell) if old_checksum != new_checksum: self.log.warning("Checksum for grade cell %s has changed!", grade_cell.grade_id) cell.source = grade_cell.source cell.metadata.nbgrader['checksum'] = grade_cell.checksum self.log.debug("Overwrote grade cell %s", grade_cell.grade_id) return cell, resources
class SaveCells(NbGraderPreprocessor): """A preprocessor to save information about grade and solution cells.""" def _create_notebook(self): notebook_info = None try: notebook = self.gradebook.find_notebook(self.notebook_id, self.assignment_id) except MissingEntry: notebook_info = {} else: # pull out existing cell ids self.old_grade_cells = set(x.name for x in notebook.grade_cells) self.old_solution_cells = set(x.name for x in notebook.solution_cells) self.old_source_cells = set(x.name for x in notebook.source_cells) # throw an error if we're trying to modify a notebook that has # submissions associated with it if len(notebook.submissions) > 0: changed = set( self.new_grade_cells.keys()) != self.old_grade_cells changed = changed | (set(self.new_solution_cells.keys()) != self.old_solution_cells) changed = changed | (set(self.new_source_cells.keys()) != self.old_source_cells) if changed: raise RuntimeError( "Cannot add or remove cells for notebook '%s' because there " "are submissions associated with it" % self.notebook_id) else: # clear data about the existing notebook self.log.debug( "Removing existing notebook '%s' from the database", self.notebook_id) notebook_info = notebook.to_dict() del notebook_info['name'] self.gradebook.remove_notebook(self.notebook_id, self.assignment_id) # create the notebook if notebook_info is not None: self.log.debug("Creating notebook '%s' in the database", self.notebook_id) self.gradebook.add_notebook(self.notebook_id, self.assignment_id, **notebook_info) # save grade cells for name, info in self.new_grade_cells.items(): grade_cell = self.gradebook.update_or_create_grade_cell( name, self.notebook_id, self.assignment_id, **info) self.log.debug("Recorded grade cell %s into the gradebook", grade_cell) # save solution cells for name, info in self.new_solution_cells.items(): solution_cell = self.gradebook.update_or_create_solution_cell( name, self.notebook_id, self.assignment_id, **info) self.log.debug("Recorded solution cell %s into the gradebook", solution_cell) # save source cells for name, info in self.new_source_cells.items(): source_cell = self.gradebook.update_or_create_source_cell( name, self.notebook_id, self.assignment_id, **info) self.log.debug("Recorded source cell %s into the gradebook", source_cell) def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.db_url = resources['nbgrader']['db_url'] if self.notebook_id == '': raise ValueError("Invalid notebook id: '{}'".format( self.notebook_id)) if self.assignment_id == '': raise ValueError("Invalid assignment id: '{}'".format( self.assignment_id)) # create a place to put new cell information self.new_grade_cells = {} self.new_solution_cells = {} self.new_source_cells = {} # connect to the database self.gradebook = Gradebook(self.db_url) nb, resources = super(SaveCells, self).preprocess(nb, resources) # create the notebook and save it to the database self._create_notebook() return nb, resources def _create_grade_cell(self, cell): grade_id = cell.metadata.nbgrader['grade_id'] try: grade_cell = self.gradebook.find_grade_cell( grade_id, self.notebook_id, self.assignment_id).to_dict() del grade_cell['name'] del grade_cell['notebook'] del grade_cell['assignment'] except MissingEntry: grade_cell = {} grade_cell.update({ 'max_score': float(cell.metadata.nbgrader['points']), 'cell_type': cell.cell_type }) self.new_grade_cells[grade_id] = grade_cell def _create_solution_cell(self, cell): grade_id = cell.metadata.nbgrader['grade_id'] try: solution_cell = self.gradebook.find_solution_cell( grade_id, self.notebook_id, self.assignment_id).to_dict() del solution_cell['name'] del solution_cell['notebook'] del solution_cell['assignment'] except MissingEntry: solution_cell = {} self.new_solution_cells[grade_id] = solution_cell def _create_source_cell(self, cell): grade_id = cell.metadata.nbgrader['grade_id'] try: source_cell = self.gradebook.find_source_cell( grade_id, self.notebook_id, self.assignment_id).to_dict() del source_cell['name'] del source_cell['notebook'] del source_cell['assignment'] except MissingEntry: source_cell = {} source_cell.update({ 'cell_type': cell.cell_type, 'locked': utils.is_locked(cell), 'source': cell.source, 'checksum': cell.metadata.nbgrader.get('checksum', None) }) self.new_source_cells[grade_id] = source_cell def preprocess_cell(self, cell, resources, cell_index): if utils.is_grade(cell): self._create_grade_cell(cell) if utils.is_solution(cell): self._create_solution_cell(cell) if utils.is_grade(cell) or utils.is_solution(cell) or utils.is_locked( cell): self._create_source_cell(cell) return cell, resources
class SaveCells(NbGraderPreprocessor): """A preprocessor to save information about grade and solution cells.""" def _create_notebook(self): notebook_info = None try: notebook = self.gradebook.find_notebook(self.notebook_id, self.assignment_id) except MissingEntry: notebook_info = {} else: # pull out existing cell ids self.old_grade_cells = set(x.name for x in notebook.grade_cells) self.old_solution_cells = set(x.name for x in notebook.solution_cells) self.old_source_cells = set(x.name for x in notebook.source_cells) # throw an error if we're trying to modify a notebook that has # submissions associated with it if len(notebook.submissions) > 0: changed = set(self.new_grade_cells.keys()) != self.old_grade_cells changed = changed | (set(self.new_solution_cells.keys()) != self.old_solution_cells) changed = changed | (set(self.new_source_cells.keys()) != self.old_source_cells) if changed: raise RuntimeError( "Cannot add or remove cells for notebook '%s' because there " "are submissions associated with it" % self.notebook_id) else: # clear data about the existing notebook self.log.debug("Removing existing notebook '%s' from the database", self.notebook_id) notebook_info = notebook.to_dict() del notebook_info['name'] self.gradebook.remove_notebook(self.notebook_id, self.assignment_id) # create the notebook if notebook_info is not None: self.log.debug("Creating notebook '%s' in the database", self.notebook_id) self.gradebook.add_notebook(self.notebook_id, self.assignment_id, **notebook_info) # save grade cells for name, info in self.new_grade_cells.items(): grade_cell = self.gradebook.update_or_create_grade_cell(name, self.notebook_id, self.assignment_id, **info) self.log.debug("Recorded grade cell %s into the gradebook", grade_cell) # save solution cells for name, info in self.new_solution_cells.items(): solution_cell = self.gradebook.update_or_create_solution_cell(name, self.notebook_id, self.assignment_id, **info) self.log.debug("Recorded solution cell %s into the gradebook", solution_cell) # save source cells for name, info in self.new_source_cells.items(): source_cell = self.gradebook.update_or_create_source_cell(name, self.notebook_id, self.assignment_id, **info) self.log.debug("Recorded source cell %s into the gradebook", source_cell) def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.db_url = resources['nbgrader']['db_url'] if self.notebook_id == '': raise ValueError("Invalid notebook id: '{}'".format(self.notebook_id)) if self.assignment_id == '': raise ValueError("Invalid assignment id: '{}'".format(self.assignment_id)) # create a place to put new cell information self.new_grade_cells = {} self.new_solution_cells = {} self.new_source_cells = {} # connect to the database self.gradebook = Gradebook(self.db_url) nb, resources = super(SaveCells, self).preprocess(nb, resources) # create the notebook and save it to the database self._create_notebook() return nb, resources def _create_grade_cell(self, cell): grade_id = cell.metadata.nbgrader['grade_id'] try: grade_cell = self.gradebook.find_grade_cell(grade_id, self.notebook_id, self.assignment_id).to_dict() del grade_cell['name'] del grade_cell['notebook'] del grade_cell['assignment'] except MissingEntry: grade_cell = {} grade_cell.update({ 'max_score': float(cell.metadata.nbgrader['points']), 'cell_type': cell.cell_type }) self.new_grade_cells[grade_id] = grade_cell def _create_solution_cell(self, cell): grade_id = cell.metadata.nbgrader['grade_id'] try: solution_cell = self.gradebook.find_solution_cell(grade_id, self.notebook_id, self.assignment_id).to_dict() del solution_cell['name'] del solution_cell['notebook'] del solution_cell['assignment'] except MissingEntry: solution_cell = {} self.new_solution_cells[grade_id] = solution_cell def _create_source_cell(self, cell): grade_id = cell.metadata.nbgrader['grade_id'] try: source_cell = self.gradebook.find_source_cell(grade_id, self.notebook_id, self.assignment_id).to_dict() del source_cell['name'] del source_cell['notebook'] del source_cell['assignment'] except MissingEntry: source_cell = {} source_cell.update({ 'cell_type': cell.cell_type, 'locked': utils.is_locked(cell), 'source': cell.source, 'checksum': cell.metadata.nbgrader.get('checksum', None) }) self.new_source_cells[grade_id] = source_cell def preprocess_cell(self, cell, resources, cell_index): if utils.is_grade(cell): self._create_grade_cell(cell) if utils.is_solution(cell): self._create_solution_cell(cell) if utils.is_grade(cell) or utils.is_solution(cell) or utils.is_locked(cell): self._create_source_cell(cell) return cell, resources
class OverwriteCells(NbGraderPreprocessor): """A preprocessor to overwrite information about grade and solution cells.""" def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.db_url = resources['nbgrader']['db_url'] # connect to the database self.gradebook = Gradebook(self.db_url) nb, resources = super(OverwriteCells, self).preprocess(nb, resources) return nb, resources def update_cell_type(self, cell, cell_type): if cell.cell_type == cell_type: return elif cell_type == 'code': cell.cell_type = 'code' cell.outputs = [] cell.execution_count = None validate(cell, 'code_cell') elif cell_type == 'markdown': cell.cell_type = 'markdown' if 'outputs' in cell: del cell['outputs'] if 'execution_count' in cell: del cell['execution_count'] validate(cell, 'markdown_cell') def report_change(self, name, attr, old, new): self.log.warning( "Attribute '%s' for cell %s has changed! (should be: %s, got: %s)", attr, name, old, new) def preprocess_cell(self, cell, resources, cell_index): grade_id = cell.metadata.get('nbgrader', {}).get('grade_id', None) if grade_id is None: return cell, resources source_cell = self.gradebook.find_source_cell(grade_id, self.notebook_id, self.assignment_id) # check that the cell type hasn't changed if cell.cell_type != source_cell.cell_type: self.report_change(grade_id, "cell_type", source_cell.cell_type, cell.cell_type) self.update_cell_type(cell, source_cell.cell_type) # check that the locked status hasn't changed if utils.is_locked(cell) != source_cell.locked: self.report_change(grade_id, "locked", source_cell.locked, utils.is_locked(cell)) cell.metadata.nbgrader["locked"] = source_cell.locked # if it's a grade cell, check that the max score hasn't changed if utils.is_grade(cell): grade_cell = self.gradebook.find_grade_cell( grade_id, self.notebook_id, self.assignment_id) old_points = float(grade_cell.max_score) new_points = float(cell.metadata.nbgrader["points"]) if old_points != new_points: self.report_change(grade_id, "points", old_points, new_points) cell.metadata.nbgrader["points"] = old_points # always update the checksum, just in case cell.metadata.nbgrader["checksum"] = source_cell.checksum # if it's locked, check that the checksum hasn't changed if source_cell.locked: old_checksum = source_cell.checksum new_checksum = utils.compute_checksum(cell) if old_checksum != new_checksum: self.report_change(grade_id, "checksum", old_checksum, new_checksum) cell.source = source_cell.source # double check the the checksum is correct now if utils.compute_checksum(cell) != source_cell.checksum: raise RuntimeError( "Inconsistent checksums for cell {}".format( source_cell.name)) return cell, resources
class OverwriteCells(NbGraderPreprocessor): """A preprocessor to overwrite information about grade and solution cells.""" def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.db_url = resources['nbgrader']['db_url'] # connect to the database self.gradebook = Gradebook(self.db_url) nb, resources = super(OverwriteCells, self).preprocess(nb, resources) return nb, resources def update_cell_type(self, cell, cell_type): if cell.cell_type == cell_type: return elif cell_type == 'code': cell.cell_type = 'code' cell.outputs = [] cell.execution_count = None validate(cell, 'code_cell') elif cell_type == 'markdown': cell.cell_type = 'markdown' if 'outputs' in cell: del cell['outputs'] if 'execution_count' in cell: del cell['execution_count'] validate(cell, 'markdown_cell') def report_change(self, name, attr, old, new): self.log.warning( "Attribute '%s' for cell %s has changed! (should be: %s, got: %s)", attr, name, old, new) def preprocess_cell(self, cell, resources, cell_index): grade_id = cell.metadata.get('nbgrader', {}).get('grade_id', None) if grade_id is None: return cell, resources source_cell = self.gradebook.find_source_cell( grade_id, self.notebook_id, self.assignment_id) # check that the cell type hasn't changed if cell.cell_type != source_cell.cell_type: self.report_change(grade_id, "cell_type", source_cell.cell_type, cell.cell_type) self.update_cell_type(cell, source_cell.cell_type) # check that the locked status hasn't changed if utils.is_locked(cell) != source_cell.locked: self.report_change(grade_id, "locked", source_cell.locked, utils.is_locked(cell)) cell.metadata.nbgrader["locked"] = source_cell.locked # if it's a grade cell, check that the max score hasn't changed if utils.is_grade(cell): grade_cell = self.gradebook.find_grade_cell( grade_id, self.notebook_id, self.assignment_id) old_points = grade_cell.max_score new_points = cell.metadata.nbgrader["points"] if old_points != new_points: self.report_change(grade_id, "points", old_points, new_points) cell.metadata.nbgrader["points"] = old_points # always update the checksum, just in case cell.metadata.nbgrader["checksum"] = source_cell.checksum # if it's locked, check that the checksum hasn't changed if source_cell.locked: old_checksum = source_cell.checksum new_checksum = utils.compute_checksum(cell) if old_checksum != new_checksum: self.report_change(grade_id, "checksum", old_checksum, new_checksum) cell.source = source_cell.source # double check the the checksum is correct now if utils.compute_checksum(cell) != source_cell.checksum: raise RuntimeError("Inconsistent checksums for cell {}".format(source_cell.name)) return cell, resources
class OverwriteCells(NbGraderPreprocessor): """A preprocessor to overwrite information about grade and solution cells.""" def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.db_url = resources['nbgrader']['db_url'] # connect to the database self.gradebook = Gradebook(self.db_url) self.comment_index = 0 nb, resources = super(OverwriteCells, self).preprocess(nb, resources) return nb, resources def update_cell_type(self, cell, cell_type): if cell.cell_type == cell_type: return elif cell_type == 'code': cell.cell_type = 'code' cell.outputs = [] cell.execution_count = None validate(cell, 'code_cell') elif cell_type == 'markdown': cell.cell_type = 'markdown' if 'outputs' in cell: del cell['outputs'] if 'execution_count' in cell: del cell['execution_count'] validate(cell, 'markdown_cell') def preprocess_cell(self, cell, resources, cell_index): if utils.is_grade(cell): grade_cell = self.gradebook.find_grade_cell( cell.metadata.nbgrader["grade_id"], self.notebook_id, self.assignment_id) cell.metadata.nbgrader['points'] = grade_cell.max_score # we only want the source and checksum for non-solution cells if not utils.is_solution(cell): old_checksum = grade_cell.checksum new_checksum = utils.compute_checksum(cell) if old_checksum != new_checksum: self.log.warning("Checksum for grade cell %s has changed!", grade_cell.name) cell.source = grade_cell.source cell.metadata.nbgrader['checksum'] = grade_cell.checksum self.update_cell_type(cell, grade_cell.cell_type) self.log.debug("Overwrote grade cell %s", grade_cell.name) if utils.is_solution(cell): solution_cell = self.gradebook.find_solution_cell( self.comment_index, self.notebook_id, self.assignment_id) old_checksum = solution_cell.checksum new_checksum = utils.compute_checksum(cell) if cell.cell_type != solution_cell.cell_type: self.log.warning("Cell type for solution cell %s has changed!", solution_cell.name) cell.metadata.nbgrader['checksum'] = solution_cell.checksum self.update_cell_type(cell, solution_cell.cell_type) self.log.debug("Overwrote solution cell #%s", self.comment_index) self.comment_index += 1 return cell, resources