def test_grade_timestamp(self, gradebook): """Is a timestamp correctly read in?""" self._copy_file("files/submitted-unchanged.ipynb", "source/ps1/p1.ipynb") run_command(["nbgrader", "assign", "ps1", "--db", gradebook]) self._copy_file("files/submitted-unchanged.ipynb", "submitted/foo/ps1/p1.ipynb") self._make_file('submitted/foo/ps1/timestamp.txt', "2015-02-02 15:58:23.948203 PST") self._copy_file("files/submitted-changed.ipynb", "submitted/bar/ps1/p1.ipynb") self._make_file('submitted/bar/ps1/timestamp.txt', "2015-02-01 14:58:23.948203 PST") run_command(["nbgrader", "autograde", "ps1", "--db", gradebook]) assert os.path.isfile("autograded/foo/ps1/p1.ipynb") assert os.path.isfile("autograded/foo/ps1/timestamp.txt") assert os.path.isfile("autograded/bar/ps1/p1.ipynb") assert os.path.isfile("autograded/bar/ps1/timestamp.txt") gb = Gradebook(gradebook) submission = gb.find_submission('ps1', 'foo') assert submission.total_seconds_late > 0 submission = gb.find_submission('ps1', 'bar') assert submission.total_seconds_late == 0 # make sure it still works to run it a second time run_command(["nbgrader", "autograde", "ps1", "--db", gradebook])
def test_add_remove_extra_notebooks(self, db): """Are extra notebooks added and removed?""" gb = Gradebook(db) assignment = gb.add_assignment("ps1") self._copy_file("files/test.ipynb", "source/ps1/test.ipynb") run_command(["nbgrader", "assign", "ps1", "--db", db]) gb.db.refresh(assignment) assert len(assignment.notebooks) == 1 notebook1 = gb.find_notebook("test", "ps1") self._copy_file("files/test.ipynb", "source/ps1/test2.ipynb") run_command(["nbgrader", "assign", "ps1", "--db", db, "--force"]) gb.db.refresh(assignment) assert len(assignment.notebooks) == 2 gb.db.refresh(notebook1) notebook2 = gb.find_notebook("test2", "ps1") os.remove("source/ps1/test2.ipynb") run_command(["nbgrader", "assign", "ps1", "--db", db, "--force"]) gb.db.refresh(assignment) assert len(assignment.notebooks) == 1 gb.db.refresh(notebook1) with pytest.raises(InvalidRequestError): gb.db.refresh(notebook2)
def _clean_old_notebooks(self, assignment_id, student_id): gb = Gradebook(self.db_url) assignment = gb.find_assignment(assignment_id) regexp = os.path.join( self._format_source("(?P<assignment_id>.*)", "(?P<student_id>.*)"), "(?P<notebook_id>.*).ipynb") # find a set of notebook ids for new notebooks new_notebook_ids = set([]) for notebook in self.notebooks: m = re.match(regexp, notebook) if m is None: raise RuntimeError("Could not match '%s' with regexp '%s'", notebook, regexp) gd = m.groupdict() if gd['assignment_id'] == assignment_id and gd['student_id'] == student_id: new_notebook_ids.add(gd['notebook_id']) # pull out the existing notebook ids old_notebook_ids = set(x.name for x in assignment.notebooks) # no added or removed notebooks, so nothing to do if old_notebook_ids == new_notebook_ids: return # some notebooks have been removed, but there are submissions associated # with the assignment, so we don't want to overwrite stuff if len(assignment.submissions) > 0: self.fail("Cannot modify existing assignment '%s' because there are submissions associated with it", assignment) # remove the old notebooks for notebook_id in (old_notebook_ids - new_notebook_ids): self.log.warning("Removing notebook '%s' from the gradebook", notebook_id) gb.remove_notebook(notebook_id, assignment_id)
def test_grade_timestamp(self, gradebook): """Is a timestamp correctly read in?""" self._copy_file("files/submitted-unchanged.ipynb", "source/ps1/p1.ipynb") run_command('nbgrader assign ps1 --db="{}" '.format(gradebook)) self._copy_file("files/submitted-unchanged.ipynb", "submitted/foo/ps1/p1.ipynb") self._make_file('submitted/foo/ps1/timestamp.txt', "2015-02-02 15:58:23.948203 PST") self._copy_file("files/submitted-changed.ipynb", "submitted/bar/ps1/p1.ipynb") self._make_file('submitted/bar/ps1/timestamp.txt', "2015-02-01 14:58:23.948203 PST") run_command('nbgrader autograde ps1 --db="{}"'.format(gradebook)) assert os.path.isfile("autograded/foo/ps1/p1.ipynb") assert os.path.isfile("autograded/foo/ps1/timestamp.txt") assert os.path.isfile("autograded/bar/ps1/p1.ipynb") assert os.path.isfile("autograded/bar/ps1/timestamp.txt") gb = Gradebook(gradebook) submission = gb.find_submission('ps1', 'foo') assert submission.total_seconds_late > 0 submission = gb.find_submission('ps1', 'bar') assert submission.total_seconds_late == 0 # make sure it still works to run it a second time run_command('nbgrader autograde ps1 --db="{}"'.format(gradebook))
def test_grade(self, gradebook): """Can files be graded?""" self._copy_file("files/submitted-unchanged.ipynb", "source/ps1/p1.ipynb") run_command('nbgrader assign ps1 --db="{}" '.format(gradebook)) self._copy_file("files/submitted-unchanged.ipynb", "submitted/foo/ps1/p1.ipynb") self._copy_file("files/submitted-changed.ipynb", "submitted/bar/ps1/p1.ipynb") run_command('nbgrader autograde ps1 --db="{}"'.format(gradebook)) assert os.path.isfile("autograded/foo/ps1/p1.ipynb") assert not os.path.isfile("autograded/foo/ps1/timestamp.txt") assert os.path.isfile("autograded/bar/ps1/p1.ipynb") assert not os.path.isfile("autograded/bar/ps1/timestamp.txt") gb = Gradebook(gradebook) notebook = gb.find_submission_notebook("p1", "ps1", "foo") assert notebook.score == 1 assert notebook.max_score == 4 assert notebook.needs_manual_grade == False comment1 = gb.find_comment(0, "p1", "ps1", "foo") comment2 = gb.find_comment(1, "p1", "ps1", "foo") assert comment1.comment == "No response." assert comment2.comment == "No response." notebook = gb.find_submission_notebook("p1", "ps1", "bar") assert notebook.score == 2 assert notebook.max_score == 4 assert notebook.needs_manual_grade == True comment1 = gb.find_comment(0, "p1", "ps1", "bar") comment2 = gb.find_comment(1, "p1", "ps1", "bar") assert comment1.comment == None assert comment2.comment == None
def test_add_remove_extra_notebooks(self, db): """Are extra notebooks added and removed?""" gb = Gradebook(db) assignment = gb.add_assignment("ps1") self._copy_file("files/test.ipynb", "source/ps1/test.ipynb") run_command('nbgrader assign ps1 --db="{}"'.format(db)) gb.db.refresh(assignment) assert len(assignment.notebooks) == 1 notebook1 = gb.find_notebook("test", "ps1") self._copy_file("files/test.ipynb", "source/ps1/test2.ipynb") run_command('nbgrader assign ps1 --db="{}" --force'.format(db)) gb.db.refresh(assignment) assert len(assignment.notebooks) == 2 gb.db.refresh(notebook1) notebook2 = gb.find_notebook("test2", "ps1") os.remove("source/ps1/test2.ipynb") run_command('nbgrader assign ps1 --db="{}" --force'.format(db)) gb.db.refresh(assignment) assert len(assignment.notebooks) == 1 gb.db.refresh(notebook1) with pytest.raises(InvalidRequestError): gb.db.refresh(notebook2)
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 test_grade_timestamp(self): """Is a timestamp correctly read in?""" with self._temp_cwd(["files/submitted-unchanged.ipynb", "files/submitted-changed.ipynb"]): dbpath = self._setup_db() os.makedirs('source/ps1') shutil.copy('submitted-unchanged.ipynb', 'source/ps1/p1.ipynb') self._run_command('nbgrader assign ps1 --db="{}" '.format(dbpath)) os.makedirs('submitted/foo/ps1') shutil.move('submitted-unchanged.ipynb', 'submitted/foo/ps1/p1.ipynb') with open('submitted/foo/ps1/timestamp.txt', 'w') as fh: fh.write("2015-02-02 15:58:23.948203 PST") os.makedirs('submitted/bar/ps1') shutil.move('submitted-changed.ipynb', 'submitted/bar/ps1/p1.ipynb') with open('submitted/bar/ps1/timestamp.txt', 'w') as fh: fh.write("2015-02-01 14:58:23.948203 PST") self._run_command('nbgrader autograde ps1 --db="{}"'.format(dbpath)) assert os.path.isfile("autograded/foo/ps1/p1.ipynb") assert os.path.isfile("autograded/foo/ps1/timestamp.txt") assert os.path.isfile("autograded/bar/ps1/p1.ipynb") assert os.path.isfile("autograded/bar/ps1/timestamp.txt") gb = Gradebook(dbpath) submission = gb.find_submission('ps1', 'foo') assert submission.total_seconds_late > 0 submission = gb.find_submission('ps1', 'bar') assert submission.total_seconds_late == 0 # make sure it still works to run it a second time self._run_command('nbgrader autograde ps1 --db="{}"'.format(dbpath))
def _setup_db(self): dbpath = self._init_db() gb = Gradebook(dbpath) gb.add_assignment("ps1", duedate="2015-02-02 14:58:23.948203 PST") gb.add_student("foo") gb.add_student("bar") return dbpath
def needs_manual_grading(self): try: gb = Gradebook('sqlite:///'+self.grader_repo_path +'/gradebook.db') subm = gb.find_submission(self.asgn.name, self.student_prefix+self.stu.canvas_id) flag = subm.needs_manual_grade finally: gb.close() return flag
def extract_grades(labSec, aNo): labSec = "AY"+labSec if len(labSec) == 1 else labSec gb = Gradebook('sqlite:////class/cs101/etc/sxns/'+labSec+'/gradebook.db') aNo = "lab" + aNo if len(aNo) == 2 else "lab0" + aNo grades = {} #print (gb.assignment_submissions) for s in gb.assignment_submissions(aNo): grades[s.student_id] = float(s.score) return grades
class GetGrades(NbGraderPreprocessor): """Preprocessor for saving grades from the database to the notebook""" def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.student_id = resources['nbgrader']['student'] self.db_url = resources['nbgrader']['db_url'] # connect to the database self.gradebook = Gradebook(self.db_url) # process the cells nb, resources = super(GetGrades, self).preprocess(nb, resources) submission = self.gradebook.find_submission_notebook( self.notebook_id, self.assignment_id, self.student_id) resources['nbgrader']['score'] = submission.score resources['nbgrader']['max_score'] = submission.max_score return nb, resources def _get_comment(self, cell, resources): """Graders can optionally add comments to the student's solutions, so add the comment information into the database if it doesn't already exist. It should NOT overwrite existing comments that might have been added by a grader already. """ # retrieve or create the comment object from the database comment = self.gradebook.find_comment( cell.metadata['nbgrader']['grade_id'], self.notebook_id, self.assignment_id, self.student_id) # save it in the notebook cell.metadata.nbgrader['comment'] = comment.comment def _get_score(self, cell, resources): grade = self.gradebook.find_grade( cell.metadata['nbgrader']['grade_id'], self.notebook_id, self.assignment_id, self.student_id) cell.metadata.nbgrader['score'] = grade.score cell.metadata.nbgrader['points'] = grade.max_score def preprocess_cell(self, cell, resources, cell_index): # if it's a solution cell, then add a comment if utils.is_solution(cell): self._get_comment(cell, resources) # if it's a grade cell, the add a grade if utils.is_grade(cell): self._get_score(cell, resources) return cell, resources
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 assignmentWithSubmissionNoMarks(assignmentWithTask: Gradebook) -> Gradebook: assignmentWithTask.add_student('hacker123') assignmentWithTask.add_student('bitdiddle') assignmentWithTask.add_student('louisreasoner') s1 = assignmentWithTask.add_submission('foo', 'hacker123') s2 = assignmentWithTask.add_submission('foo', 'bitdiddle') s1.flagged = True s2.flagged = False assignmentWithTask.db.commit() return assignmentWithTask
def test_save_cells(self, db): """Ensure cells are saved into the database""" self._copy_file("files/test.ipynb", "source/ps1/test.ipynb") gb = Gradebook(db) gb.add_assignment("ps1") run_command(["nbgrader", "assign", "ps1", "--db", db]) notebook = gb.find_notebook("test", "ps1") assert len(notebook.grade_cells) == 6
def test_save_cells(self, db): """Ensure cells are saved into the database""" self._copy_file("files/test.ipynb", "source/ps1/test.ipynb") gb = Gradebook(db) gb.add_assignment("ps1") run_command('nbgrader assign ps1 --db="{}"'.format(db)) notebook = gb.find_notebook("test", "ps1") assert len(notebook.grade_cells) == 8
def init_assignment(self, assignment_id, student_id): super(AutogradeApp, self).init_assignment(assignment_id, student_id) # try to get the student from the database, and throw an error if it # doesn't exist gb = Gradebook(self.db_url) try: gb.find_student(student_id) except MissingEntry: if self.create_student: self.log.warning("Creating student with ID '%s'", student_id) gb.add_student(student_id) else: self.fail("No student with ID '%s' exists in the database", student_id) # try to read in a timestamp from file src_path = self._format_source(assignment_id, student_id) timestamp = self._get_existing_timestamp(src_path) if timestamp: submission = gb.update_or_create_submission( assignment_id, student_id, timestamp=timestamp) self.log.info("%s submitted at %s", submission, timestamp) # if the submission is late, print out how many seconds late it is if timestamp and submission.total_seconds_late > 0: self.log.warning("%s is %s seconds late", submission, submission.total_seconds_late) else: submission = gb.update_or_create_submission(assignment_id, student_id) # copy files over from the source directory self.log.info("Overwriting files with master versions from the source directory") dest_path = self._format_dest(assignment_id, student_id) source_path = self.directory_structure.format( nbgrader_step=self.source_directory, student_id='.', assignment_id=assignment_id) source_files = utils.find_all_files(source_path, self.ignore + ["*.ipynb"]) # copy them to the build directory for filename in source_files: dest = os.path.join(dest_path, os.path.relpath(filename, source_path)) ensure_dir_exists(os.path.dirname(dest)) if not os.path.normpath(dest) == os.path.normpath(filename): self.log.info("Linking %s -> %s", filename, dest) link_or_copy(filename, dest) # ignore notebooks that aren't in the database notebooks = [] for notebook in self.notebooks: notebook_id = os.path.splitext(os.path.basename(notebook))[0] try: gb.find_notebook(notebook_id, assignment_id) except MissingEntry: self.log.warning("Skipping unknown notebook: %s", notebook) continue else: notebooks.append(notebook) self.notebooks = notebooks
def test_find_comment(assignmentWithSubmissionWithMarks: Gradebook) -> None: s = assignmentWithSubmissionWithMarks.find_submission('foo', 'hacker123') for n in s.notebooks: comments = n.comments for c1 in comments: c2 = assignmentWithSubmissionWithMarks.find_comment(c1.name, n.name, 'foo', 'hacker123') assert c1 == c2 with pytest.raises(MissingEntry): assignmentWithSubmissionWithMarks.find_comment('asdf', n.name, 'foo', 'hacker123')
def test_remove_student_db(self, tmpdir_factory): self._mock_remove_student() tmp_dir = tmpdir_factory.mktemp(self.course_id) os.chdir(tmp_dir) self._add_empty_gradebook(tmp_dir) gb = Gradebook('sqlite:///gradebook.db', course_id=self.course_id) gb.add_student(self.student_id) # test valid cm.main(['remove_students', self.course_id, self.student_id]) assert len(gb.students) == 0
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 preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.student_id = resources['nbgrader']['student'] self.db_url = resources['nbgrader']['db_url'] # connect to the database self.gradebook = Gradebook(self.db_url) # process the cells nb, resources = super(SaveAutoGrades, self).preprocess(nb, resources) return nb, resources
def init_assignment(self, assignment_id, student_id): super(AssignApp, self).init_assignment(assignment_id, student_id) # try to get the assignment from the database, and throw an error if it # doesn't exist gb = Gradebook(self.db_url) try: gb.find_assignment(assignment_id) except MissingEntry: if self.create_assignment: self.log.warning("Creating assignment '%s'", assignment_id) gb.add_assignment(assignment_id) else: self.fail("No assignment called '%s' exists in the database", assignment_id)
def test_save_cells(self): """Ensure cells are saved into the database""" with self._temp_cwd(["files/test.ipynb"]): os.makedirs('source/ps1') shutil.move("test.ipynb", "source/ps1/test.ipynb") dbpath = self._init_db() gb = Gradebook(dbpath) gb.add_assignment("ps1") self._run_command('nbgrader assign ps1 --db="{}"'.format(dbpath)) notebook = gb.find_notebook("test", "ps1") assert_equal(len(notebook.grade_cells), 8)
def check_needs_manual_grading(course, anm, stu, grader): gradebook_file = os.path.join(course['course_storage_path'], grader, course['instructor_repo_path'], course['gradebook_filename']) gb = Gradebook('sqlite:///' + gradebook_file) try: subm = gb.find_submission(anm, course['student_name_prefix'] + stu) flag = subm.needs_manual_grade except MissingEntry as e: print(e) finally: gb.close() return flag
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 get_max_nb_score(self): """Return the max code score from the gradebook""" nb_name = os.path.splitext(self.nb_filename)[0] with Gradebook(self.db_path) as gb: nb = gb.find_notebook(nb_name, self.pset_id) max_score = nb.max_code_score return max_score
def _retrieve_grades_from_db(self): db_url = Path(self.gradebook_dir, 'gradebook.db') # raise an error if the database does not exist if not db_url.exists(): logger.error( f'Gradebook database file does not exist at: {db_url}.') raise GradesSenderCriticalError out = [] max_score = 0 # Create the connection to the gradebook database with Gradebook(f'sqlite:///{db_url}', course_id=self.course_id) as gb: try: # retrieve the assignment record assignment_row = gb.find_assignment(self.assignment_name) max_score = assignment_row.max_score submissions = gb.assignment_submissions(self.assignment_name) logger.info( f'Found {len(submissions)} submissions for assignment: {self.assignment_name}' ) except MissingEntry as e: logger.info( 'Assignment or Submission is missing in database: %s' % e) raise GradesSenderMissingInfoError for submission in submissions: # retrieve the student to use the lms id student = gb.find_student(submission.student_id) out.append({ 'score': submission.score, 'lms_user_id': student.lms_user_id }) logger.info(f'Grades found: {out}') logger.info('max_score for this assignment %s' % max_score) return max_score, out
def _get_db(cls, course_id: str) -> Gradebook: ''' Create new connection to sqlite database. ''' return Gradebook(f'sqlite:////home/{grader / course_id}/grader.db', course_id=course_id)
def register_assignment(self, assignment_name: str, **kwargs: dict) -> Assignment: """ Adds an assignment to nbgrader database Args: assignment_name: The assingment's name Raises: InvalidEntry: when there was an error adding the assignment to the database """ if not assignment_name: raise ValueError('assignment_name missing') logger.debug('Assignment name normalized %s to save in gradebook' % assignment_name) assignment = None with Gradebook(self.db_url, course_id=self.course_id) as gb: try: assignment = gb.update_or_create_assignment( assignment_name, **kwargs) logger.debug('Added assignment %s to gradebook' % assignment_name) except InvalidEntry as e: logger.debug( 'Error ocurred by adding assignment to gradebook: %s' % e) return assignment
def gradebook(self): gb = self.settings['nbgrader_gradebook'] if gb is None: self.log.debug("creating gradebook") gb = Gradebook(self.db_url, self.coursedir.course_id) self.settings['nbgrader_gradebook'] = gb return gb
def upload_grade(self, canvas, failed = False): if self.grade_uploaded: print('Grade already uploaded. Returning') return SubmissionStatus.GRADE_UPLOADED print('Uploading grade for submission ' + self.asgn.name+':'+self.stu.canvas_id) if failed: score = 0 else: try: gb = Gradebook('sqlite:///'+self.grader_repo_path +'/gradebook.db') subm = gb.find_submission(self.asgn.name, self.student_prefix+self.stu.canvas_id) score = subm.score except Exception as e: print('Error when accessing grade from gradebook db') print(e) self.error = e return SubmissionStatus.ERROR finally: gb.close() try: max_score = self.compute_max_score() except Exception as e: print('Error when trying to compute max score from release notebook') print(e) self.error = e return SubmissionStatus.ERROR self.score = score self.max_score = max_score pct = "{:.2f}".format(100*score/max_score) print('Student ' + self.stu.canvas_id + ' assignment ' + self.asgn.name + ' score: ' + str(score) + (' [HARDFAIL]' if failed else '')) print('Assignment ' + self.asgn.name + ' max score: ' + str(max_score)) print('Pct Score: ' + pct) print('Posting to canvas...') try: canvas.put_grade(self.asgn.canvas_id, self.stu.canvas_id, pct) except GradeNotUploadedError as e: print('Error when uploading grade') print(e.message) self.error = e return SubmissionStatus.ERROR self.grade_uploaded = True return SubmissionStatus.GRADE_UPLOADED
class SaveGradeCells(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(SaveGradeCells, self).preprocess(nb, resources) return nb, resources def preprocess_cell(self, cell, resources, cell_index): if utils.is_grade(cell): grade_cell = self.gradebook.find_or_create_grade_cell( grade_id=cell.metadata.nbgrader.grade_id, notebook_id=self.notebook_id, assignment=self.assignment) grade_cell.max_score = float(cell.metadata.nbgrader['points']) # we only want the source and checksum for non-solution cells if utils.is_solution(cell): grade_cell.source = None grade_cell.checksum = None else: grade_cell.source = cell.source grade_cell.checksum = cell.metadata.nbgrader['checksum'] self.gradebook.update_grade_cell(grade_cell) self.log.debug("Recorded grade cell %s into database", grade_cell.grade_id) return cell, resources
def get_course(self) -> Course: """ Gets the course model instance """ with Gradebook(self.db_url, course_id=self.course_id) as gb: course = gb.check_course(self.course_id) logger.debug(f'course got from db:{course}') return course
def get_total_score(self): """Return total student score for notebook""" nb_name = os.path.splitext(self.nb_filename)[0] with Gradebook(self.db_path) as gb: nb = gb.find_submission_notebook(nb_name, self.pset_id, self.username) score = nb.code_score return score
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
def gradebook(request, tempdir): # create a "class files" directory origdir = os.getcwd() os.mkdir("class_files") os.chdir("class_files") # copy files from the user guide source_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "docs", "source", "user_guide", "source") submitted_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "docs", "source", "user_guide", "submitted") shutil.copytree(os.path.join(os.path.dirname(__file__), source_path), "source") shutil.copytree(os.path.join(os.path.dirname(__file__), submitted_path), "submitted") # create the gradebook gb = Gradebook("sqlite:///gradebook.db") gb.add_assignment("Problem Set 1") gb.add_student("Bitdiddle", first_name="Ben", last_name="B") gb.add_student("Hacker", first_name="Alyssa", last_name="H") gb.add_student("Reasoner", first_name="Louis", last_name="R") # run nbgrader assign run_command([ "nbgrader", "assign", "Problem Set 1", "--IncludeHeaderFooter.header=source/header.ipynb" ]) # run the autograder run_command(["nbgrader", "autograde", "Problem Set 1"]) def fin(): os.chdir(origdir) shutil.rmtree("class_files") request.addfinalizer(fin) return gb
def init_assignment(self, assignment_id, student_id): super(AssignApp, self).init_assignment(assignment_id, student_id) # try to get the assignment from the database, and throw an error if it # doesn't exist gb = Gradebook(self.db_url) try: gb.find_assignment(assignment_id) except MissingEntry: if self.create_assignment: self.log.warning("Creating assignment '%s'", assignment_id) gb.add_assignment(assignment_id) else: self.fail("No assignment called '%s' exists in the database", assignment_id) else: # check if there are any extra notebooks in the db that are no longer # part of the assignment, and if so, remove them if self.notebook_id == "*": self._clean_old_notebooks(assignment_id, student_id)
def preprocess(self, nb, resources): # pull information from the resources self.notebook_id = resources['nbgrader']['notebook'] self.assignment_id = resources['nbgrader']['assignment'] self.student_id = resources['nbgrader']['student'] self.db_url = resources['nbgrader']['db_url'] # connect to the database self.gradebook = Gradebook(self.db_url) # process the cells nb, resources = super(GetGrades, self).preprocess(nb, resources) submission = self.gradebook.find_submission_notebook( self.notebook_id, self.assignment_id, self.student_id) resources['nbgrader']['score'] = submission.score resources['nbgrader']['max_score'] = submission.max_score return nb, resources
def get_section_scores(self): """Return student score for each problem in notebook""" grades = [] nb_name = os.path.splitext(self.nb_filename)[0] with Gradebook(self.db_path) as gb: nb = gb.find_submission_notebook(nb_name, self.pset_id, self.username) for problem in nb.grades: grades.append(problem.to_dict()) return grades
def GetAssignmentList(): assignment_list = [] with Gradebook('sqlite:///test/gradebook.db') as gb: for assignment in gb.assignments: temp_dict = {} temp_dict["assignment"] = assignment.name temp_dict["problem"] = [] for notebook in assignment.notebooks: temp_dict["problem"].append(notebook.name) assignment_list.append(temp_dict) return jsonify(assignment_list)
def export(self, gradebook: Gradebook) -> None: """Creates a CSV file from nbgrader's gradebook. Args: gradebook (Gradebook): an nbgrader gradebook instance. """ if self.canvas_export == "": dest = "canvas_grades.csv" else: dest = self.to if self.canvas_import == "": canvas_import = "canvas.csv" else: canvas_import = self.canvas_import self.log.info("Exporting grades to %s", dest) with open(canvas_import, "r") as csv_file, open(dest, "w") as op_csv_file: csv_reader = csv.DictReader(csv_file) fields = csv_reader.fieldnames csv_writer = csv.DictWriter(op_csv_file, fields) csv_writer.writeheader() for row in csv_reader: if "Points Possible" in row["Student"]: self.log.info("Skipping second row") csv_writer.writerow(row) continue self.log.info("Finding student with ID %s", row["ID"]) for column in fields: if " (" not in column: continue assignment_name = column.split(" (")[0] self.log.info( "Finding submission of Student '%s' for Assignment '%s'", row["ID"], assignment_name, ) submission = None try: submission = gradebook.find_submission( assignment_name, row["ID"] ) row[column] = max( 0.0, submission.score - submission.late_submission_penalty ) except MissingEntry: self.log.info( "Submission of Student '%s' for Assignment '%s' not found", row["ID"], assignment_name, ) continue csv_writer.writerow(row)
def extractGrades(gradebookDB, section, labNo): ''' Open the specified grade database and return grades. Input: filename str Output: grades dict ''' import sys from nbgrader.api import Gradebook gb = Gradebook(gradebookDB) #gb = Gradebook( f'sqlite:////class/cs101/etc/sxns/{section}/gradebook.db') grades = {} for s in gb.assignment_submissions("lab" + labNo): grades[s.student_id] = float(s.score) return grades
def test_update_or_create_solution_cell(gradebook: Gradebook) -> None: # first test creating it gradebook.add_assignment('foo') gradebook.add_notebook('p1', 'foo') sc1 = gradebook.update_or_create_solution_cell('test1', 'p1', 'foo') assert gradebook.find_solution_cell('test1', 'p1', 'foo') == sc1 # now test finding/updating it sc2 = gradebook.update_or_create_solution_cell('test1', 'p1', 'foo') assert sc1 == sc2
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 test_grade(self): """Can files be graded?""" with self._temp_cwd(["files/submitted-unchanged.ipynb", "files/submitted-changed.ipynb"]): dbpath = self._setup_db() os.makedirs('source/ps1') shutil.copy('submitted-unchanged.ipynb', 'source/ps1/p1.ipynb') self._run_command('nbgrader assign ps1 --db="{}" '.format(dbpath)) os.makedirs('submitted/foo/ps1') shutil.move('submitted-unchanged.ipynb', 'submitted/foo/ps1/p1.ipynb') os.makedirs('submitted/bar/ps1') shutil.move('submitted-changed.ipynb', 'submitted/bar/ps1/p1.ipynb') self._run_command('nbgrader autograde ps1 --db="{}"'.format(dbpath)) assert os.path.isfile("autograded/foo/ps1/p1.ipynb") assert not os.path.isfile("autograded/foo/ps1/timestamp.txt") assert os.path.isfile("autograded/bar/ps1/p1.ipynb") assert not os.path.isfile("autograded/bar/ps1/timestamp.txt") gb = Gradebook(dbpath) notebook = gb.find_submission_notebook("p1", "ps1", "foo") assert_equal(notebook.score, 1) assert_equal(notebook.max_score, 4) assert_equal(notebook.needs_manual_grade, False) comment1 = gb.find_comment(0, "p1", "ps1", "foo") comment2 = gb.find_comment(1, "p1", "ps1", "foo") assert_equal(comment1.comment, "No response.") assert_equal(comment2.comment, "No response.") notebook = gb.find_submission_notebook("p1", "ps1", "bar") assert_equal(notebook.score, 2) assert_equal(notebook.max_score, 4) assert_equal(notebook.needs_manual_grade, True) comment1 = gb.find_comment(0, "p1", "ps1", "bar") comment2 = gb.find_comment(1, "p1", "ps1", "bar") assert_equal(comment1.comment, None) assert_equal(comment2.comment, None)
def class_files(request, tempdir): # copy files from the user guide source_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "docs", "source", "user_guide", "source") shutil.copytree(os.path.join(os.path.dirname(__file__), source_path), "source") # create a fake ps1 os.mkdir(os.path.join("source", "ps1")) with open(os.path.join("source", "ps1", "problem 1.ipynb"), "w") as fh: write_nb(new_notebook(), fh, 4) # create the gradebook gb = Gradebook("sqlite:///gradebook.db") gb.add_assignment("Problem Set 1") gb.add_assignment("ps1") gb.add_student("Bitdiddle", first_name="Ben", last_name="B") gb.add_student("Hacker", first_name="Alyssa", last_name="H") gb.add_student("Reasoner", first_name="Louis", last_name="R") return tempdir