From 39f6ac13a763ec619213fa1b92737819282fb6b9 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Thu, 31 Aug 2023 14:22:40 -0700 Subject: [PATCH] remove tests to speed things up --- nbgrader/tests/api/__init__.py | 0 nbgrader/tests/api/test_gradebook.py | 1312 ----------------- nbgrader/tests/api/test_models.py | 1278 ---------------- nbgrader/tests/apps/__init__.py | 0 nbgrader/tests/apps/base.py | 71 - nbgrader/tests/apps/conftest.py | 134 -- nbgrader/tests/apps/files/__init__.py | 0 .../apps/files/autotest-hashed-changed.ipynb | 112 -- .../files/autotest-hashed-unchanged.ipynb | 110 -- .../tests/apps/files/autotest-hashed.ipynb | 82 -- .../files/autotest-hidden-changed-right.ipynb | 85 -- .../files/autotest-hidden-changed-wrong.ipynb | 85 -- .../files/autotest-hidden-unchanged.ipynb | 83 -- .../tests/apps/files/autotest-hidden.ipynb | 80 - .../apps/files/autotest-multi-changed.ipynb | 267 ---- .../apps/files/autotest-multi-unchanged.ipynb | 257 ---- .../tests/apps/files/autotest-multi.ipynb | 164 --- .../apps/files/autotest-simple-changed.ipynb | 92 -- .../files/autotest-simple-unchanged.ipynb | 90 -- .../tests/apps/files/autotest-simple.ipynb | 74 - nbgrader/tests/apps/files/autotests.yml | 310 ---- nbgrader/tests/apps/files/data.txt | 4 - nbgrader/tests/apps/files/gradebook.db | Bin 35840 -> 0 bytes .../files/infinite-loop-with-output.ipynb | 26 - nbgrader/tests/apps/files/infinite-loop.ipynb | 25 - nbgrader/tests/apps/files/jupyter.png | Bin 21090 -> 0 bytes nbgrader/tests/apps/files/myexporter.py | 6 - nbgrader/tests/apps/files/notebooks.zip | Bin 18018 -> 0 bytes .../tests/apps/files/open_relative_file.ipynb | 37 - nbgrader/tests/apps/files/side-effects.ipynb | 25 - .../tests/apps/files/submitted-changed.ipynb | 157 -- .../submitted-cheat-attempt-alternative.ipynb | 158 -- .../apps/files/submitted-cheat-attempt.ipynb | 158 -- .../files/submitted-grade-cell-changed.ipynb | 157 -- .../files/submitted-locked-cell-changed.ipynb | 158 -- .../apps/files/submitted-unchanged.ipynb | 157 -- .../tests/apps/files/test-hidden-tests.ipynb | 258 ---- .../files/test-no-metadata-autotest.ipynb | 225 --- .../tests/apps/files/test-no-metadata.ipynb | 229 --- .../tests/apps/files/test-v0-invalid.ipynb | 314 ---- nbgrader/tests/apps/files/test-v0.ipynb | 315 ---- nbgrader/tests/apps/files/test-v1.ipynb | 300 ---- nbgrader/tests/apps/files/test-v2.ipynb | 300 ---- .../tests/apps/files/test-with-output.ipynb | 322 ---- nbgrader/tests/apps/files/test.ipynb | 300 ---- nbgrader/tests/apps/files/timeout.ipynb | 70 - nbgrader/tests/apps/files/timestamp.txt | 1 - nbgrader/tests/apps/files/too-new.ipynb | 32 - .../validating-environment-variable.ipynb | 55 - .../apps/files/validation-zero-points.ipynb | 75 - nbgrader/tests/apps/test_api.py | 818 ---------- nbgrader/tests/apps/test_config.py | 17 - nbgrader/tests/apps/test_nbgrader.py | 38 - .../tests/apps/test_nbgrader_autograde.py | 1287 ---------------- nbgrader/tests/apps/test_nbgrader_collect.py | 197 --- nbgrader/tests/apps/test_nbgrader_db.py | 423 ------ nbgrader/tests/apps/test_nbgrader_export.py | 65 - .../apps/test_nbgrader_fetch_assignment.py | 115 -- .../tests/apps/test_nbgrader_fetchfeedback.py | 93 -- .../tests/apps/test_nbgrader_formgrade.py | 9 - .../apps/test_nbgrader_generate_assignment.py | 432 ------ .../apps/test_nbgrader_generate_config.py | 30 - .../apps/test_nbgrader_generate_feedback.py | 383 ----- .../apps/test_nbgrader_generate_solution.py | 142 -- nbgrader/tests/apps/test_nbgrader_list.py | 500 ------- .../tests/apps/test_nbgrader_quickstart.py | 162 -- .../apps/test_nbgrader_releaseassignment.py | 106 -- .../apps/test_nbgrader_releasefeedback.py | 138 -- nbgrader/tests/apps/test_nbgrader_submit.py | 251 ---- nbgrader/tests/apps/test_nbgrader_update.py | 114 -- nbgrader/tests/apps/test_nbgrader_validate.py | 189 --- .../tests/apps/test_nbgrader_zip_collect.py | 691 --------- .../preprocessors/test_checkcellmetadata.py | 103 -- .../preprocessors/test_clearhiddentests.py | 207 --- .../preprocessors/test_clearmarkscheme.py | 184 --- .../preprocessors/test_clearsolutions.py | 238 --- .../preprocessors/test_computechecksums.py | 119 -- .../preprocessors/test_deduplicateids.py | 53 - .../tests/preprocessors/test_getgrades.py | 133 -- .../tests/preprocessors/test_headerfooter.py | 48 - .../preprocessors/test_instantiatetests.py | 4 +- .../tests/preprocessors/test_limitoutput.py | 38 - .../tests/preprocessors/test_lockcells.py | 170 --- .../preprocessors/test_overwritecells.py | 267 ---- .../preprocessors/test_overwritekernelspec.py | 59 - .../preprocessors/test_saveautogrades.py | 216 --- .../tests/preprocessors/test_savecells.py | 311 ---- 87 files changed, 2 insertions(+), 16898 deletions(-) delete mode 100644 nbgrader/tests/api/__init__.py delete mode 100644 nbgrader/tests/api/test_gradebook.py delete mode 100644 nbgrader/tests/api/test_models.py delete mode 100644 nbgrader/tests/apps/__init__.py delete mode 100644 nbgrader/tests/apps/base.py delete mode 100644 nbgrader/tests/apps/conftest.py delete mode 100644 nbgrader/tests/apps/files/__init__.py delete mode 100644 nbgrader/tests/apps/files/autotest-hashed-changed.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-hashed-unchanged.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-hashed.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-hidden-changed-right.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-hidden-changed-wrong.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-hidden-unchanged.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-hidden.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-multi-changed.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-multi-unchanged.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-multi.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-simple-changed.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-simple-unchanged.ipynb delete mode 100644 nbgrader/tests/apps/files/autotest-simple.ipynb delete mode 100644 nbgrader/tests/apps/files/autotests.yml delete mode 100644 nbgrader/tests/apps/files/data.txt delete mode 100644 nbgrader/tests/apps/files/gradebook.db delete mode 100644 nbgrader/tests/apps/files/infinite-loop-with-output.ipynb delete mode 100644 nbgrader/tests/apps/files/infinite-loop.ipynb delete mode 100644 nbgrader/tests/apps/files/jupyter.png delete mode 100644 nbgrader/tests/apps/files/myexporter.py delete mode 100644 nbgrader/tests/apps/files/notebooks.zip delete mode 100644 nbgrader/tests/apps/files/open_relative_file.ipynb delete mode 100644 nbgrader/tests/apps/files/side-effects.ipynb delete mode 100644 nbgrader/tests/apps/files/submitted-changed.ipynb delete mode 100644 nbgrader/tests/apps/files/submitted-cheat-attempt-alternative.ipynb delete mode 100644 nbgrader/tests/apps/files/submitted-cheat-attempt.ipynb delete mode 100644 nbgrader/tests/apps/files/submitted-grade-cell-changed.ipynb delete mode 100644 nbgrader/tests/apps/files/submitted-locked-cell-changed.ipynb delete mode 100644 nbgrader/tests/apps/files/submitted-unchanged.ipynb delete mode 100644 nbgrader/tests/apps/files/test-hidden-tests.ipynb delete mode 100644 nbgrader/tests/apps/files/test-no-metadata-autotest.ipynb delete mode 100644 nbgrader/tests/apps/files/test-no-metadata.ipynb delete mode 100644 nbgrader/tests/apps/files/test-v0-invalid.ipynb delete mode 100644 nbgrader/tests/apps/files/test-v0.ipynb delete mode 100644 nbgrader/tests/apps/files/test-v1.ipynb delete mode 100644 nbgrader/tests/apps/files/test-v2.ipynb delete mode 100644 nbgrader/tests/apps/files/test-with-output.ipynb delete mode 100644 nbgrader/tests/apps/files/test.ipynb delete mode 100644 nbgrader/tests/apps/files/timeout.ipynb delete mode 100644 nbgrader/tests/apps/files/timestamp.txt delete mode 100644 nbgrader/tests/apps/files/too-new.ipynb delete mode 100644 nbgrader/tests/apps/files/validating-environment-variable.ipynb delete mode 100644 nbgrader/tests/apps/files/validation-zero-points.ipynb delete mode 100644 nbgrader/tests/apps/test_api.py delete mode 100644 nbgrader/tests/apps/test_config.py delete mode 100644 nbgrader/tests/apps/test_nbgrader.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_autograde.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_collect.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_db.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_export.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_fetch_assignment.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_fetchfeedback.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_formgrade.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_generate_assignment.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_generate_config.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_generate_feedback.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_generate_solution.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_list.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_quickstart.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_releaseassignment.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_releasefeedback.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_submit.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_update.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_validate.py delete mode 100644 nbgrader/tests/apps/test_nbgrader_zip_collect.py delete mode 100644 nbgrader/tests/preprocessors/test_checkcellmetadata.py delete mode 100644 nbgrader/tests/preprocessors/test_clearhiddentests.py delete mode 100644 nbgrader/tests/preprocessors/test_clearmarkscheme.py delete mode 100644 nbgrader/tests/preprocessors/test_clearsolutions.py delete mode 100644 nbgrader/tests/preprocessors/test_computechecksums.py delete mode 100644 nbgrader/tests/preprocessors/test_deduplicateids.py delete mode 100644 nbgrader/tests/preprocessors/test_getgrades.py delete mode 100644 nbgrader/tests/preprocessors/test_headerfooter.py delete mode 100644 nbgrader/tests/preprocessors/test_limitoutput.py delete mode 100644 nbgrader/tests/preprocessors/test_lockcells.py delete mode 100644 nbgrader/tests/preprocessors/test_overwritecells.py delete mode 100644 nbgrader/tests/preprocessors/test_overwritekernelspec.py delete mode 100644 nbgrader/tests/preprocessors/test_saveautogrades.py delete mode 100644 nbgrader/tests/preprocessors/test_savecells.py diff --git a/nbgrader/tests/api/__init__.py b/nbgrader/tests/api/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/nbgrader/tests/api/test_gradebook.py b/nbgrader/tests/api/test_gradebook.py deleted file mode 100644 index 0b80ae262..000000000 --- a/nbgrader/tests/api/test_gradebook.py +++ /dev/null @@ -1,1312 +0,0 @@ -import pytest - -from datetime import datetime, timedelta -from ... import api -from ... import utils -from ...api import InvalidEntry, MissingEntry -from _pytest.fixtures import SubRequest -from nbgrader.api import Gradebook - - -@pytest.fixture -def gradebook(request: SubRequest) -> Gradebook: - gb = api.Gradebook("sqlite:///:memory:") - - def fin() -> None: - gb.close() - request.addfinalizer(fin) - return gb - - -@pytest.fixture -def assignment(gradebook: Gradebook) -> Gradebook: - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gradebook.add_grade_cell('test1', 'p1', 'foo', max_score=1, cell_type='code') - gradebook.add_grade_cell('test2', 'p1', 'foo', max_score=2, cell_type='markdown') - gradebook.add_solution_cell('solution1', 'p1', 'foo') - gradebook.add_solution_cell('test2', 'p1', 'foo') - gradebook.add_source_cell('test1', 'p1', 'foo', cell_type='code') - gradebook.add_source_cell('test2', 'p1', 'foo', cell_type='markdown') - gradebook.add_source_cell('solution1', 'p1', 'foo', cell_type='code') - return gradebook - - -def makeAssignments(gb, na, nn, ns, grades=[1, 2, 10, 20, 100, 200]): - for si in range(ns): - sname = "s{0}".format(si + 1) - gb.add_student(sname) - for ia in range(na): - aname = 'a{0}'.format(ia + 1) - a = gb.add_assignment(aname) - for ni in range(nn): - nname = 'n{0}'.format(ni + 1) - n = gb.add_notebook(nname, aname) - gb.add_solution_cell('solution1', nname, aname) - gb.add_solution_cell('solution2', nname, aname) - gb.add_source_cell('source1', nname, aname, cell_type='code') - gb.add_source_cell('source2', nname, aname, cell_type='markdown') - gb.add_source_cell('solution1', nname, aname, cell_type='code') - gb.add_grade_cell('grade_code1', nname, aname, cell_type='code', max_score=2) - gb.add_grade_cell('grade_code2', nname, aname, cell_type='code', max_score=3) - gb.add_grade_cell('grade_written1', nname, aname, cell_type='markdown', max_score=20) - gb.add_grade_cell('grade_written2', nname, aname, cell_type='markdown', max_score=30) - gb.add_task_cell('task1', nname, aname, cell_type='markdown', max_score=200) - gb.add_task_cell('task2', nname, aname, cell_type='markdown', max_score=300) - for si in range(ns): - sname = "s{0}".format(si + 1) - sub = gb.add_submission(aname, sname) - sub.flagged = False - for ni in range(nn): - nname = 'n{0}'.format(ni + 1) - g1 = gb.find_grade("grade_code1", nname, aname, sname) - g2 = gb.find_grade("grade_code2", nname, aname, sname) - g3 = gb.find_grade("grade_written1", nname, aname, sname) - g4 = gb.find_grade("grade_written2", nname, aname, sname) - g5 = gb.find_grade("task1", nname, aname, sname) - g6 = gb.find_grade("task2", nname, aname, sname) - - (g1.manual_score, g2.manual_score, g3.manual_score, g4.manual_score, - g5.manual_score, g6.manual_score) = grades - gb.db.commit() - - return gb - - -@pytest.fixture -def FiveStudents(gradebook): - return makeAssignments(gradebook, 1, 1, 5) - - -@pytest.fixture -def FiveNotebooks(gradebook): - return makeAssignments(gradebook, 1, 5, 1) - - -@pytest.fixture -def FiveAssignments(gradebook): - return makeAssignments(gradebook, 5, 1, 1) - - -@pytest.fixture -def assignmentWithTask(gradebook: Gradebook) -> Gradebook: - for f in ['foo', 'foo2']: - gradebook.add_assignment(f) - for n in ['p1', 'p2']: - gradebook.add_notebook(n, f) - gradebook.add_solution_cell('solution1', n, f) - gradebook.add_solution_cell('test2', n, f) - gradebook.add_source_cell('test1', n, f, cell_type='code') - gradebook.add_source_cell('test2', n, f, cell_type='markdown') - gradebook.add_source_cell('solution1', n, f, cell_type='code') - gradebook.add_grade_cell('grade_code1', n, f, cell_type='code', max_score=1) - gradebook.add_grade_cell('grade_code2', n, f, cell_type='code', max_score=10) - gradebook.add_grade_cell('grade_written1', n, f, cell_type='markdown', max_score=1) - gradebook.add_grade_cell('grade_written2', n, f, cell_type='markdown', max_score=10) - gradebook.add_task_cell('task1', n, f, cell_type='markdown', max_score=2) - gradebook.add_task_cell('task2', n, f, cell_type='markdown', max_score=20) - - return gradebook - - -@pytest.fixture -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 - -possiblegrades = [ - [0.5, 2, 3, 5, 1, 7, 2, 1], - [0.1, 4, 0.25, 1, 7, 0.0, 1, 1], - [0] * 8, - [2] * 8, - [0.25] * 8, -] - - -@pytest.fixture(params=possiblegrades) -def assignmentWithSubmissionWithMarks(assignmentWithSubmissionNoMarks: Gradebook, request: SubRequest) -> Gradebook: - a = assignmentWithSubmissionNoMarks - g1 = a.find_grade("grade_code1", "p1", "foo", "bitdiddle") - g2 = a.find_grade("grade_code2", "p1", "foo", "bitdiddle") - - g3 = a.find_grade("grade_written1", "p1", "foo", "hacker123") - g4 = a.find_grade("grade_written2", "p1", "foo", "hacker123") - - g5 = a.find_grade("task1", "p1", "foo", "bitdiddle") - g6 = a.find_grade("task2", "p1", "foo", "bitdiddle") - g7 = a.find_grade("task1", "p1", "foo", "hacker123") - g8 = a.find_grade("task2", "p1", "foo", "hacker123") - - (g1.manual_score, g2.manual_score, g3.manual_score, g4.manual_score, g5.manual_score, - g6.manual_score, g7.manual_score, g8.manual_score) = request.param - a.db.commit() - a.usedgrades = request.param - a.usedgrades_code = request.param[:2] - a.usedgrades_written = request.param[2:4] - a.usedgrades_task = request.param[4:] - - return a - - -@pytest.fixture -def assignmentManyStudents(assignmentWithTask, request): - a = assignmentWithTask - for s in range(50): - sname = 's{0}'.format(s) - a.add_student(sname) - sub = a.add_submission('foo', sname) - g1 = a.find_grade("grade_code1", "p1", "foo", sname) - g2 = a.find_grade("grade_written1", "p1", "foo", sname) - g3 = a.find_grade("task1", "p1", "foo", sname) - g4 = a.find_grade("task2", "p1", "foo", sname) - - ( - g1.manual_score, - g2.manual_score, - g3.manual_score, - g4.manual_score) = (1, 2, 3, 4) - a.db.commit() - - return a - - -@pytest.fixture -def assignmentTwoStudents(assignmentWithTask, request): - a = assignmentWithTask - for s in range(50): - sname = 's{0}'.format(s) - a.add_student(sname) - sub = a.add_submission('foo', sname) - g1 = a.find_grade("grade_code1", "p1", "foo", sname) - g2 = a.find_grade("grade_written1", "p1", "foo", sname) - g3 = a.find_grade("task1", "p1", "foo", sname) - g4 = a.find_grade("task2", "p1", "foo", sname) - - ( - g1.manual_score, - g2.manual_score, - g3.manual_score, - g4.manual_score) = (1, 2, 3, 4) - a.db.commit() - - return a - - -def test_init(gradebook: Gradebook) -> None: - assert gradebook.students == [] - assert gradebook.assignments == [] - - -# Test students - -def test_add_student(gradebook): - s = gradebook.add_student('12345') - assert s.id == '12345' - assert gradebook.students == [s] - - # try adding a duplicate student - with pytest.raises(InvalidEntry): - gradebook.add_student('12345') - - # try adding a student with arguments - s = gradebook.add_student('6789', last_name="Bar", first_name="Foo", email="foo@bar.com") - assert s.id == '6789' - assert s.last_name == "Bar" - assert s.first_name == "Foo" - assert s.email == "foo@bar.com" - - -def test_add_duplicate_student(gradebook): - # we also need this test because this will cause an IntegrityError - # under the hood rather than a FlushError - gradebook.add_student('12345') - with pytest.raises(InvalidEntry): - gradebook.add_student('12345') - - -def test_find_student(gradebook): - s1 = gradebook.add_student('12345') - assert gradebook.find_student('12345') == s1 - - s2 = gradebook.add_student('abcd') - assert gradebook.find_student('12345') == s1 - assert gradebook.find_student('abcd') == s2 - - -def test_find_nonexistant_student(gradebook): - with pytest.raises(MissingEntry): - gradebook.find_student('12345') - - -def test_remove_student(assignment): - assignment.add_student('hacker123') - assignment.add_submission('foo', 'hacker123') - - assignment.remove_student('hacker123') - - with pytest.raises(MissingEntry): - assignment.find_submission('foo', 'hacker123') - with pytest.raises(MissingEntry): - assignment.find_student('hacker123') - - -def test_update_or_create_student(gradebook): - # first test creating it - s1 = gradebook.update_or_create_student('hacker123') - assert gradebook.find_student('hacker123') == s1 - assert s1.first_name is None - - # now test finding/updating it - s2 = gradebook.update_or_create_student('hacker123', first_name='Alyssa') - assert s1 == s2 - assert s2.first_name == 'Alyssa' - - -# Test assignments - -def test_add_assignment(gradebook): - a = gradebook.add_assignment('foo') - assert a.name == 'foo' - assert gradebook.assignments == [a] - - # try adding a duplicate assignment - with pytest.raises(InvalidEntry): - gradebook.add_assignment('foo') - - # try adding an assignment with arguments - now = datetime.utcnow() - a = gradebook.add_assignment('bar', duedate=now) - assert a.name == 'bar' - assert a.duedate == now - - # try adding with a string timestamp - a = gradebook.add_assignment('baz', duedate=now.isoformat()) - assert a.name == 'baz' - assert a.duedate == now - - -def test_add_duplicate_assignment(gradebook): - gradebook.add_assignment('foo') - with pytest.raises(InvalidEntry): - gradebook.add_assignment('foo') - - -def test_find_assignment(gradebook): - a1 = gradebook.add_assignment('foo') - assert gradebook.find_assignment('foo') == a1 - - a2 = gradebook.add_assignment('bar') - assert gradebook.find_assignment('foo') == a1 - assert gradebook.find_assignment('bar') == a2 - - -def test_find_nonexistant_assignment(gradebook): - with pytest.raises(MissingEntry): - gradebook.find_assignment('foo') - - -def test_remove_assignment(assignment): - assignment.add_student('hacker123') - assignment.add_submission('foo', 'hacker123') - - notebooks = assignment.find_assignment('foo').notebooks - grade_cells = [x for nb in notebooks for x in nb.grade_cells] - solution_cells = [x for nb in notebooks for x in nb.solution_cells] - source_cells = [x for nb in notebooks for x in nb.source_cells] - - assignment.remove_assignment('foo') - - for nb in notebooks: - assert assignment.db.query(api.SubmittedNotebook).filter(api.SubmittedNotebook.id == nb.id).all() == [] - for grade_cell in grade_cells: - assert assignment.db.query(api.GradeCell).filter(api.GradeCell.id == grade_cell.id).all() == [] - for solution_cell in solution_cells: - assert assignment.db.query(api.SolutionCell).filter(api.SolutionCell.id == solution_cell.id).all() == [] - for source_cell in source_cells: - assert assignment.db.query(api.SourceCell).filter(api.SourceCell.id == source_cell.id).all() == [] - - with pytest.raises(MissingEntry): - assignment.find_assignment('foo') - - assert assignment.find_student('hacker123').submissions == [] - - -def test_update_or_create_assignment(gradebook): - # first test creating it - a1 = gradebook.update_or_create_assignment('foo') - assert gradebook.find_assignment('foo') == a1 - assert a1.duedate is None - - # now test finding/updating it - a2 = gradebook.update_or_create_assignment('foo', duedate="2015-02-02 14:58:23.948203 America/Los_Angeles") - assert a1 == a2 - assert a2.duedate == utils.parse_utc("2015-02-02 14:58:23.948203 America/Los_Angeles") - -# Test notebooks - - -def test_add_notebook(gradebook): - a = gradebook.add_assignment('foo') - n = gradebook.add_notebook('p1', 'foo') - assert n.name == 'p1' - assert n.assignment == a - assert a.notebooks == [n] - - # try adding a duplicate assignment - with pytest.raises(InvalidEntry): - gradebook.add_notebook('p1', 'foo') - - -def test_add_duplicate_notebook(gradebook): - # it should be ok to add a notebook with the same name, as long as - # it's for different assignments - gradebook.add_assignment('foo') - gradebook.add_assignment('bar') - n1 = gradebook.add_notebook('p1', 'foo') - n2 = gradebook.add_notebook('p1', 'bar') - assert n1.id != n2.id - - # but not ok to add a notebook with the same name for the same assignment - with pytest.raises(InvalidEntry): - gradebook.add_notebook('p1', 'foo') - - -def test_find_notebook(gradebook): - gradebook.add_assignment('foo') - n1 = gradebook.add_notebook('p1', 'foo') - assert gradebook.find_notebook('p1', 'foo') == n1 - - n2 = gradebook.add_notebook('p2', 'foo') - assert gradebook.find_notebook('p1', 'foo') == n1 - assert gradebook.find_notebook('p2', 'foo') == n2 - - -def test_find_nonexistant_notebook(gradebook: Gradebook) -> None: - # check that it doesn't find it when there is nothing in the db - with pytest.raises(MissingEntry): - gradebook.find_notebook('p1', 'foo') - - # check that it doesn't find it even if the assignment exists - gradebook.add_assignment('foo') - with pytest.raises(MissingEntry): - gradebook.find_notebook('p1', 'foo') - - -def test_update_or_create_notebook(gradebook): - # first test creating it - gradebook.add_assignment('foo') - n1 = gradebook.update_or_create_notebook('p1', 'foo') - assert gradebook.find_notebook('p1', 'foo') == n1 - - # now test finding/updating it - n2 = gradebook.update_or_create_notebook('p1', 'foo') - assert n1 == n2 - - -def test_remove_notebook(assignment): - assignment.add_student('hacker123') - assignment.add_submission('foo', 'hacker123') - - notebooks = assignment.find_assignment('foo').notebooks - - for nb in notebooks: - grade_cells = [x for x in nb.grade_cells] - solution_cells = [x for x in nb.solution_cells] - source_cells = [x for x in nb.source_cells] - - assignment.remove_notebook(nb.name, 'foo') - assert assignment.db.query(api.SubmittedNotebook).filter(api.SubmittedNotebook.id == nb.id).all() == [] - - for grade_cell in grade_cells: - assert assignment.db.query(api.GradeCell).filter(api.GradeCell.id == grade_cell.id).all() == [] - for solution_cell in solution_cells: - assert assignment.db.query(api.SolutionCell).filter(api.SolutionCell.id == solution_cell.id).all() == [] - for source_cell in source_cells: - assert assignment.db.query(api.SourceCell).filter(api.SourceCell.id == source_cell.id).all() == [] - - with pytest.raises(MissingEntry): - assignment.find_notebook(nb.name, 'foo') - - -def test_course_id_constructor(): - gb = api.Gradebook("sqlite:///:memory:") - assert gb.db.query(api.Course).first().id == "default_course" - -def test_course_id_multiple_assignments(): - course_one = "course-one" - course_two = "course-two" - - gb_one = api.Gradebook("sqlite:///:memory:", course_id=course_one) - gb_two = api.Gradebook("sqlite:///:memory:", course_id=course_two) - - assignment_one = gb_one.add_assignment('foo') - assignent_two = gb_two.add_assignment('bar') - - assert assignment_one.course_id == course_one - assert assignent_two.course_id == course_two - - assert len(gb_one.db.query(api.Course).all()) == 1 - assert gb_one.db.query(api.Course).first().id == course_one - assert gb_one.db.query(api.Assignment).first().course_id == course_one - assert gb_one.db.query(api.Assignment).first().course == gb_one.db.query(api.Course).first() - - assert len(gb_two.db.query(api.Course).all()) == 1 - assert gb_two.db.query(api.Course).first().id == course_two - assert gb_two.db.query(api.Assignment).first().course_id == course_two - assert gb_two.db.query(api.Assignment).first().course == gb_two.db.query(api.Course).first() - -# Test grade cells - -def test_add_grade_cell(gradebook): - gradebook.add_assignment('foo') - n = gradebook.add_notebook('p1', 'foo') - gc = gradebook.add_grade_cell('test1', 'p1', 'foo', max_score=2, cell_type='markdown') - assert gc.name == 'test1' - assert gc.max_score == 2 - assert gc.cell_type == 'markdown' - assert n.grade_cells == [gc] - assert gc.notebook == n - - -def test_add_grade_cell_with_args(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc = gradebook.add_grade_cell( - 'test1', 'p1', 'foo', - max_score=3, cell_type="code") - assert gc.name == 'test1' - assert gc.max_score == 3 - assert gc.cell_type == "code" - - -def test_create_invalid_grade_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - with pytest.raises(InvalidEntry): - gradebook.add_grade_cell( - 'test1', 'p1', 'foo', - max_score=3, cell_type="something") - - -def test_add_duplicate_grade_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gradebook.add_grade_cell('test1', 'p1', 'foo', max_score=1, cell_type='code') - with pytest.raises(InvalidEntry): - gradebook.add_grade_cell('test1', 'p1', 'foo', max_score=2, cell_type='markdown') - - -def test_find_grade_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc1 = gradebook.add_grade_cell('test1', 'p1', 'foo', max_score=1, cell_type='code') - assert gradebook.find_grade_cell('test1', 'p1', 'foo') == gc1 - - gc2 = gradebook.add_grade_cell('test2', 'p1', 'foo', max_score=2, cell_type='code') - assert gradebook.find_grade_cell('test1', 'p1', 'foo') == gc1 - assert gradebook.find_grade_cell('test2', 'p1', 'foo') == gc2 - - -def test_find_nonexistant_grade_cell(gradebook): - with pytest.raises(MissingEntry): - gradebook.find_grade_cell('test1', 'p1', 'foo') - - gradebook.add_assignment('foo') - with pytest.raises(MissingEntry): - gradebook.find_grade_cell('test1', 'p1', 'foo') - - gradebook.add_notebook('p1', 'foo') - with pytest.raises(MissingEntry): - gradebook.find_grade_cell('test1', 'p1', 'foo') - - -def test_update_or_create_grade_cell(gradebook): - # first test creating it - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc1 = gradebook.update_or_create_grade_cell('test1', 'p1', 'foo', max_score=2, cell_type='code') - assert gc1.max_score == 2 - assert gc1.cell_type == 'code' - assert gradebook.find_grade_cell('test1', 'p1', 'foo') == gc1 - - # now test finding/updating it - gc2 = gradebook.update_or_create_grade_cell('test1', 'p1', 'foo', max_score=3) - assert gc1 == gc2 - assert gc1.max_score == 3 - assert gc1.cell_type == 'code' - - -# Test solution cells - -def test_add_solution_cell(gradebook): - gradebook.add_assignment('foo') - n = gradebook.add_notebook('p1', 'foo') - sc = gradebook.add_solution_cell('test1', 'p1', 'foo') - assert sc.name == 'test1' - assert n.solution_cells == [sc] - assert sc.notebook == n - - -def test_add_duplicate_solution_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gradebook.add_solution_cell('test1', 'p1', 'foo') - with pytest.raises(InvalidEntry): - gradebook.add_solution_cell('test1', 'p1', 'foo') - - -def test_find_solution_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - sc1 = gradebook.add_solution_cell('test1', 'p1', 'foo') - assert gradebook.find_solution_cell('test1', 'p1', 'foo') == sc1 - - sc2 = gradebook.add_solution_cell('test2', 'p1', 'foo') - assert gradebook.find_solution_cell('test1', 'p1', 'foo') == sc1 - assert gradebook.find_solution_cell('test2', 'p1', 'foo') == sc2 - - -def test_find_nonexistant_solution_cell(gradebook): - with pytest.raises(MissingEntry): - gradebook.find_solution_cell('test1', 'p1', 'foo') - - gradebook.add_assignment('foo') - with pytest.raises(MissingEntry): - gradebook.find_solution_cell('test1', 'p1', 'foo') - - gradebook.add_notebook('p1', 'foo') - with pytest.raises(MissingEntry): - gradebook.find_solution_cell('test1', 'p1', 'foo') - - -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 - - -# Test source cells - -def test_add_source_cell(gradebook): - gradebook.add_assignment('foo') - n = gradebook.add_notebook('p1', 'foo') - sc = gradebook.add_source_cell('test1', 'p1', 'foo', cell_type="code") - assert sc.name == 'test1' - assert sc.cell_type == 'code' - assert n.source_cells == [sc] - assert sc.notebook == n - - -def test_add_source_cell_with_args(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - sc = gradebook.add_source_cell( - 'test1', 'p1', 'foo', - source="blah blah blah", - cell_type="code", checksum="abcde") - assert sc.name == 'test1' - assert sc.source == "blah blah blah" - assert sc.cell_type == "code" - assert sc.checksum == "abcde" - - -def test_create_invalid_source_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - with pytest.raises(InvalidEntry): - gradebook.add_source_cell( - 'test1', 'p1', 'foo', - source="blah blah blah", - cell_type="something", checksum="abcde") - - -def test_add_duplicate_source_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gradebook.add_source_cell('test1', 'p1', 'foo', cell_type="code") - with pytest.raises(InvalidEntry): - gradebook.add_source_cell('test1', 'p1', 'foo', cell_type="code") - - -def test_find_source_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - sc1 = gradebook.add_source_cell('test1', 'p1', 'foo', cell_type="code") - assert gradebook.find_source_cell('test1', 'p1', 'foo') == sc1 - - sc2 = gradebook.add_source_cell('test2', 'p1', 'foo', cell_type="code") - assert gradebook.find_source_cell('test1', 'p1', 'foo') == sc1 - assert gradebook.find_source_cell('test2', 'p1', 'foo') == sc2 - - -def test_find_nonexistant_source_cell(gradebook): - with pytest.raises(MissingEntry): - gradebook.find_source_cell('test1', 'p1', 'foo') - - gradebook.add_assignment('foo') - with pytest.raises(MissingEntry): - gradebook.find_source_cell('test1', 'p1', 'foo') - - gradebook.add_notebook('p1', 'foo') - with pytest.raises(MissingEntry): - gradebook.find_source_cell('test1', 'p1', 'foo') - - -def test_update_or_create_source_cell(gradebook): - # first test creating it - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - sc1 = gradebook.update_or_create_source_cell('test1', 'p1', 'foo', cell_type='code') - assert sc1.cell_type == 'code' - assert gradebook.find_source_cell('test1', 'p1', 'foo') == sc1 - - # now test finding/updating it - assert sc1.checksum is None - sc2 = gradebook.update_or_create_source_cell('test1', 'p1', 'foo', checksum="123456") - assert sc1 == sc2 - assert sc1.cell_type == 'code' - assert sc1.checksum == "123456" - - -# Test submissions - -def test_add_submission(assignment): - assignment.add_student('hacker123') - assignment.add_student('bitdiddle') - s1 = assignment.add_submission('foo', 'hacker123') - s2 = assignment.add_submission('foo', 'bitdiddle') - - assert assignment.assignment_submissions('foo') == [s2, s1] - assert assignment.student_submissions('hacker123') == [s1] - assert assignment.student_submissions('bitdiddle') == [s2] - assert assignment.find_submission('foo', 'hacker123') == s1 - assert assignment.find_submission('foo', 'bitdiddle') == s2 - - -def test_add_duplicate_submission(assignment): - assignment.add_student('hacker123') - assignment.add_submission('foo', 'hacker123') - with pytest.raises(InvalidEntry): - assignment.add_submission('foo', 'hacker123') - - -def test_remove_submission(assignment): - assignment.add_student('hacker123') - assignment.add_submission('foo', 'hacker123') - - submission = assignment.find_submission('foo', 'hacker123') - notebooks = submission.notebooks - grades = [x for nb in notebooks for x in nb.grades] - comments = [x for nb in notebooks for x in nb.comments] - - assignment.remove_submission('foo', 'hacker123') - - for nb in notebooks: - assert assignment.db.query(api.SubmittedNotebook).filter(api.SubmittedNotebook.id == nb.id).all() == [] - for grade in grades: - assert assignment.db.query(api.Grade).filter(api.Grade.id == grade.id).all() == [] - for comment in comments: - assert assignment.db.query(api.Comment).filter(api.Comment.id == comment.id).all() == [] - - with pytest.raises(MissingEntry): - assignment.find_submission('foo', 'hacker123') - - -def test_update_or_create_submission(assignment): - assignment.add_student('hacker123') - s1 = assignment.update_or_create_submission('foo', 'hacker123') - assert s1.timestamp is None - - s2 = assignment.update_or_create_submission('foo', 'hacker123', timestamp="2015-02-02 14:58:23.948203 America/Los_Angeles") - assert s1 == s2 - assert s2.timestamp == utils.parse_utc("2015-02-02 14:58:23.948203 America/Los_Angeles") - - -def test_find_submission_notebook(assignment): - assignment.add_student('hacker123') - s = assignment.add_submission('foo', 'hacker123') - n1, = s.notebooks - - with pytest.raises(MissingEntry): - assignment.find_submission_notebook('p2', 'foo', 'hacker123') - - n2 = assignment.find_submission_notebook('p1', 'foo', 'hacker123') - assert n1 == n2 - - -def test_find_submission_notebook_by_id(assignment): - assignment.add_student('hacker123') - s = assignment.add_submission('foo', 'hacker123') - n1, = s.notebooks - - with pytest.raises(MissingEntry): - assignment.find_submission_notebook_by_id('12345') - - n2 = assignment.find_submission_notebook_by_id(n1.id) - assert n1 == n2 - - -def test_remove_submission_notebook(assignment): - assignment.add_student('hacker123') - assignment.add_submission('foo', 'hacker123') - - submission = assignment.find_submission('foo', 'hacker123') - notebooks = submission.notebooks - - for nb in notebooks: - grades = [x for x in nb.grades] - comments = [x for x in nb.comments] - - assignment.remove_submission_notebook(nb.name, 'foo', 'hacker123') - assert assignment.db.query(api.SubmittedNotebook).filter(api.SubmittedNotebook.id == nb.id).all() == [] - - for grade in grades: - assert assignment.db.query(api.Grade).filter(api.Grade.id == grade.id).all() == [] - for comment in comments: - assert assignment.db.query(api.Comment).filter(api.Comment.id == comment.id).all() == [] - - with pytest.raises(MissingEntry): - assignment.find_submission_notebook(nb.name, 'foo', 'hacker123') - - -def test_find_grade(assignment): - assignment.add_student('hacker123') - s = assignment.add_submission('foo', 'hacker123') - n1, = s.notebooks - grades = n1.grades - - for g1 in grades: - g2 = assignment.find_grade(g1.name, 'p1', 'foo', 'hacker123') - assert g1 == g2 - - with pytest.raises(MissingEntry): - assignment.find_grade('asdf', 'p1', 'foo', 'hacker123') - - -def test_find_grade_by_id(assignment): - assignment.add_student('hacker123') - s = assignment.add_submission('foo', 'hacker123') - n1, = s.notebooks - grades = n1.grades - - for g1 in grades: - g2 = assignment.find_grade_by_id(g1.id) - assert g1 == g2 - - with pytest.raises(MissingEntry): - assignment.find_grade_by_id('12345') - - -def test_find_comment(assignment): - assignment.add_student('hacker123') - s = assignment.add_submission('foo', 'hacker123') - n1, = s.notebooks - comments = n1.comments - - for c1 in comments: - c2 = assignment.find_comment(c1.name, 'p1', 'foo', 'hacker123') - assert c1 == c2 - - with pytest.raises(MissingEntry): - assignment.find_comment('asdf', 'p1', 'foo', 'hacker123') - - -def test_find_comment_by_id(assignment): - assignment.add_student('hacker123') - s = assignment.add_submission('foo', 'hacker123') - n1, = s.notebooks - comments = n1.comments - - for c1 in comments: - c2 = assignment.find_comment_by_id(c1.id) - assert c1 == c2 - - with pytest.raises(MissingEntry): - assignment.find_comment_by_id('12345') - - -# Test average scores - -def test_average_assignment_score(assignment): - assert assignment.average_assignment_score('foo') == 0.0 - assert assignment.average_assignment_code_score('foo') == 0.0 - assert assignment.average_assignment_written_score('foo') == 0.0 - - assignment.add_student('hacker123') - assignment.add_student('bitdiddle') - assignment.add_submission('foo', 'hacker123') - assignment.add_submission('foo', 'bitdiddle') - - assert assignment.average_assignment_score('foo') == 0.0 - assert assignment.average_assignment_code_score('foo') == 0.0 - assert assignment.average_assignment_written_score('foo') == 0.0 - - g1 = assignment.find_grade("test1", "p1", "foo", "hacker123") - g2 = assignment.find_grade("test2", "p1", "foo", "hacker123") - g3 = assignment.find_grade("test1", "p1", "foo", "bitdiddle") - g4 = assignment.find_grade("test2", "p1", "foo", "bitdiddle") - - g1.manual_score = 0.5 - g2.manual_score = 2 - g3.manual_score = 1 - g4.manual_score = 1 - assignment.db.commit() - - assert assignment.average_assignment_score('foo') == 2.25 - assert assignment.average_assignment_code_score('foo') == 0.75 - assert assignment.average_assignment_written_score('foo') == 1.5 - - -def test_average_notebook_score(assignment: Gradebook) -> None: - assert assignment.average_notebook_score('p1', 'foo') == 0 - assert assignment.average_notebook_code_score('p1', 'foo') == 0 - assert assignment.average_notebook_written_score('p1', 'foo') == 0 - - assignment.add_student('hacker123') - assignment.add_student('bitdiddle') - assignment.add_submission('foo', 'hacker123') - assignment.add_submission('foo', 'bitdiddle') - - assert assignment.average_notebook_score('p1', 'foo') == 0.0 - assert assignment.average_notebook_code_score('p1', 'foo') == 0.0 - assert assignment.average_notebook_written_score('p1', 'foo') == 0.0 - - g1 = assignment.find_grade("test1", "p1", "foo", "hacker123") - g2 = assignment.find_grade("test2", "p1", "foo", "hacker123") - g3 = assignment.find_grade("test1", "p1", "foo", "bitdiddle") - g4 = assignment.find_grade("test2", "p1", "foo", "bitdiddle") - - g1.manual_score = 0.5 - g2.manual_score = 2 - g3.manual_score = 1 - g4.manual_score = 1 - assignment.db.commit() - - assert assignment.average_notebook_score('p1', 'foo') == 2.25 - assert assignment.average_notebook_code_score('p1', 'foo') == 0.75 - assert assignment.average_notebook_written_score('p1', 'foo') == 1.5 - - -# Test mass dictionary queries - -def test_student_dicts(assignment): - assignment.add_student('hacker123') - assignment.add_student('bitdiddle') - assignment.add_student('louisreasoner') - assignment.add_submission('foo', 'hacker123') - assignment.add_submission('foo', 'bitdiddle') - - g1 = assignment.find_grade("test1", "p1", "foo", "hacker123") - g2 = assignment.find_grade("test2", "p1", "foo", "hacker123") - g3 = assignment.find_grade("test1", "p1", "foo", "bitdiddle") - g4 = assignment.find_grade("test2", "p1", "foo", "bitdiddle") - - g1.manual_score = 0.5 - g2.manual_score = 2 - g3.manual_score = 1 - g4.manual_score = 1 - assignment.db.commit() - - students = assignment.student_dicts() - a = sorted(students, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in assignment.students], key=lambda x: x["id"]) - assert a == b - - -def test_student_dicts_zero_points(gradebook): - gradebook.add_assignment("ps1") - s = gradebook.add_student("1234") - assert gradebook.student_dicts() == [s.to_dict()] - - -def test_notebook_submission_dicts(assignment): - assignment.add_student('hacker123') - assignment.add_student('bitdiddle') - s1 = assignment.add_submission('foo', 'hacker123') - s2 = assignment.add_submission('foo', 'bitdiddle') - s1.flagged = True - s2.flagged = False - - g1 = assignment.find_grade("test1", "p1", "foo", "hacker123") - g2 = assignment.find_grade("test2", "p1", "foo", "hacker123") - g3 = assignment.find_grade("test1", "p1", "foo", "bitdiddle") - g4 = assignment.find_grade("test2", "p1", "foo", "bitdiddle") - - g1.manual_score = 0.5 - g2.manual_score = 2 - g3.manual_score = 1 - g4.manual_score = 1 - assignment.db.commit() - - notebook = assignment.find_notebook("p1", "foo") - submissions = assignment.notebook_submission_dicts("p1", "foo") - a = sorted(submissions, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in notebook.submissions], key=lambda x: x["id"]) - assert a == b - - -def test_submission_dicts(assignment): - assignment.add_student('hacker123') - assignment.add_student('bitdiddle') - s1 = assignment.add_submission('foo', 'hacker123') - s2 = assignment.add_submission('foo', 'bitdiddle') - s1.flagged = True - s2.flagged = False - - g1 = assignment.find_grade("test1", "p1", "foo", "hacker123") - g2 = assignment.find_grade("test2", "p1", "foo", "hacker123") - g3 = assignment.find_grade("test1", "p1", "foo", "bitdiddle") - g4 = assignment.find_grade("test2", "p1", "foo", "bitdiddle") - - g1.manual_score = 0.5 - g2.manual_score = 2 - g3.manual_score = 1 - g4.manual_score = 1 - assignment.db.commit() - - a = sorted(assignment.submission_dicts("foo"), key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in assignment.find_assignment("foo").submissions], key=lambda x: x["id"]) - assert a == b - - -def test_grant_extension(gradebook): - gradebook.add_assignment("ps1", duedate="2018-05-09 10:00:00") - gradebook.add_student("hacker123") - s1 = gradebook.add_submission("ps1", "hacker123") - assert s1.extension is None - assert s1.duedate == datetime(2018, 5, 9, 10, 0, 0) - - gradebook.grant_extension('ps1', 'hacker123', minutes=10) - assert s1.extension == timedelta(minutes=10) - assert s1.duedate == datetime(2018, 5, 9, 10, 10, 0) - - gradebook.grant_extension('ps1', 'hacker123', hours=1) - assert s1.extension == timedelta(hours=1) - assert s1.duedate == datetime(2018, 5, 9, 11, 0, 0) - - gradebook.grant_extension('ps1', 'hacker123', days=2) - assert s1.extension == timedelta(days=2) - assert s1.duedate == datetime(2018, 5, 11, 10, 0, 0) - - gradebook.grant_extension('ps1', 'hacker123', weeks=3) - assert s1.extension == timedelta(weeks=3) - assert s1.duedate == datetime(2018, 5, 30, 10, 0, 0) - - gradebook.grant_extension('ps1', 'hacker123') - assert s1.extension is None - assert s1.duedate == datetime(2018, 5, 9, 10, 0, 0) - - -# Test task cells - -def test_add_task_cell(gradebook): - gradebook.add_assignment('foo') - n = gradebook.add_notebook('p1', 'foo') - gc = gradebook.add_task_cell('test1', 'p1', 'foo', max_score=2, cell_type='markdown') - assert gc.name == 'test1' - assert gc.max_score == 2 - assert gc.cell_type == 'markdown' - assert n.task_cells == [gc] - assert gc.notebook == n - - -def test_add_task_cell_with_args(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc = gradebook.add_task_cell( - 'test1', 'p1', 'foo', - max_score=3, cell_type="code") - assert gc.name == 'test1' - assert gc.max_score == 3 - assert gc.cell_type == "code" - - -def test_create_invalid_task_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - with pytest.raises(InvalidEntry): - gradebook.add_task_cell( - 'test1', 'p1', 'foo', - max_score=3, cell_type="something") - - -def test_add_duplicate_task_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gradebook.add_task_cell('test1', 'p1', 'foo', max_score=1, cell_type='code') - with pytest.raises(InvalidEntry): - gradebook.add_task_cell('test1', 'p1', 'foo', max_score=2, cell_type='markdown') - - -def test_find_task_cell(gradebook): - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc1 = gradebook.add_task_cell('test1', 'p1', 'foo', max_score=1, cell_type='code') - assert gradebook.find_task_cell('test1', 'p1', 'foo') == gc1 - - gc2 = gradebook.add_task_cell('test2', 'p1', 'foo', max_score=2, cell_type='code') - assert gradebook.find_task_cell('test1', 'p1', 'foo') == gc1 - assert gradebook.find_task_cell('test2', 'p1', 'foo') == gc2 - - -def test_find_nonexistant_task_cell(gradebook): - with pytest.raises(MissingEntry): - gradebook.find_task_cell('test1', 'p1', 'foo') - - gradebook.add_assignment('foo') - with pytest.raises(MissingEntry): - gradebook.find_task_cell('test1', 'p1', 'foo') - - gradebook.add_notebook('p1', 'foo') - with pytest.raises(MissingEntry): - gradebook.find_task_cell('test1', 'p1', 'foo') - - -def test_update_or_create_task_cell(gradebook): - # first test creating it - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc1 = gradebook.update_or_create_task_cell('test1', 'p1', 'foo', max_score=2, cell_type='markdown') - assert gc1.max_score == 2 - assert gc1.cell_type == 'markdown' - assert gradebook.find_task_cell('test1', 'p1', 'foo') == gc1 - - # now test finding/updating it - gc2 = gradebook.update_or_create_task_cell('test1', 'p1', 'foo', max_score=3) - assert gc1 == gc2 - assert gc1.max_score == 3 - assert gc1.cell_type == 'markdown' - - -def test_find_graded_cell(gradebook): - # first test creating it - gradebook.add_assignment('foo') - gradebook.add_assignment('foo2') - gradebook.add_notebook('p1', 'foo') - gradebook.add_notebook('p2', 'foo2') - gc1 = gradebook.update_or_create_task_cell('test1', 'p1', 'foo', max_score=2, cell_type='markdown') - assert gc1.max_score == 2 - assert gc1.cell_type == 'markdown' - assert gradebook.find_graded_cell('test1', 'p1', 'foo') == gc1 - gc2 = gradebook.update_or_create_grade_cell('test2', 'p2', 'foo2', max_score=2, cell_type='code') - assert gc2.max_score == 2 - assert gc2.cell_type == 'code' - assert gradebook.find_grade_cell('test2', 'p2', 'foo2') == gc2 - assert gradebook.find_graded_cell('test2', 'p2', 'foo2') == gc2 - - -def test_grade_cell_maxscore(gradebook): - # first test creating it - gradebook.add_assignment('foo') - gradebook.add_notebook('p1', 'foo') - gc1 = gradebook.update_or_create_task_cell('test1', 'p1', 'foo', max_score=1000, cell_type='markdown') - gc1a = gradebook.update_or_create_task_cell('test1a', 'p1', 'foo', max_score=3000, cell_type='markdown') - gc2 = gradebook.update_or_create_grade_cell('test2', 'p1', 'foo', max_score=5, cell_type='code') - gc3 = gradebook.update_or_create_grade_cell('test3', 'p1', 'foo', max_score=7, cell_type='code') - gc4 = gradebook.update_or_create_grade_cell('test4', 'p1', 'foo', max_score=13, cell_type='code') - gc5 = gradebook.update_or_create_grade_cell('test5', 'p1', 'foo', max_score=10, cell_type='code') - # assert gc2.max_score == 5 - n1 = gradebook.find_notebook('p1', 'foo') - assert n1.max_score_gradecell == 35 - assert n1.max_score_taskcell == 4000 - assert n1.max_score == 4035 - - -def test_grades_include_taskcells(assignmentWithSubmissionWithMarks: Gradebook) -> None: - s = assignmentWithSubmissionWithMarks.find_submission('foo', 'hacker123') - for n in s.notebooks: - grades = n.grades - assert len(grades) == 6 - - -# next 4 same as in normal tests, but with an assignment with tasks -def test_find_grade(assignmentWithSubmissionWithMarks): - s = assignmentWithSubmissionWithMarks.find_submission('foo', 'hacker123') - for n in s.notebooks: - grades = n.grades - for g1 in grades: - g2 = assignmentWithSubmissionWithMarks.find_grade(g1.name, n.name, 'foo', 'hacker123') - assert g1 == g2 - - with pytest.raises(MissingEntry): - assignmentWithSubmissionWithMarks.find_grade('asdf', 'p1', 'foo', 'hacker123') - - -def test_find_grade_by_id(assignmentWithSubmissionWithMarks): - s = assignmentWithSubmissionWithMarks.find_submission('foo', 'hacker123') - for n in s.notebooks: - grades = n.grades - - for g1 in grades: - g2 = assignmentWithSubmissionWithMarks.find_grade_by_id(g1.id) - assert g1 == g2 - - with pytest.raises(MissingEntry): - assignmentWithSubmissionWithMarks.find_grade_by_id('12345') - - -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_find_comment_by_id(assignmentWithSubmissionWithMarks): - s = assignmentWithSubmissionWithMarks.find_submission('foo', 'hacker123') - for n in s.notebooks: - comments = n.comments - - for c1 in comments: - c2 = assignmentWithSubmissionWithMarks.find_comment_by_id(c1.id) - assert c1 == c2 - - with pytest.raises(MissingEntry): - assignmentWithSubmissionWithMarks.find_comment_by_id('12345') - - -def test_average_assignment_score_empty(assignment): - assert assignment.average_assignment_score('foo') == 0.0 - assert assignment.average_assignment_code_score('foo') == 0.0 - assert assignment.average_assignment_written_score('foo') == 0.0 - assert assignment.average_assignment_task_score('foo') == 0.0 - - -def test_average_assignment_no_score(assignmentWithSubmissionNoMarks): - assert assignmentWithSubmissionNoMarks.average_assignment_score('foo') == 0.0 - assert assignmentWithSubmissionNoMarks.average_assignment_code_score('foo') == 0.0 - assert assignmentWithSubmissionNoMarks.average_assignment_written_score('foo') == 0.0 - assert assignmentWithSubmissionNoMarks.average_assignment_task_score('foo') == 0.0 - - -def test_average_assignment_with_score(assignmentWithSubmissionWithMarks): - assert assignmentWithSubmissionWithMarks.average_assignment_score('foo') == sum(assignmentWithSubmissionWithMarks.usedgrades) / 2.0 - assert assignmentWithSubmissionWithMarks.average_assignment_code_score('foo') == sum(assignmentWithSubmissionWithMarks.usedgrades_code) / 2.0 - assert assignmentWithSubmissionWithMarks.average_assignment_written_score('foo') == sum(assignmentWithSubmissionWithMarks.usedgrades_written) / 2.0 - assert assignmentWithSubmissionWithMarks.average_assignment_task_score('foo') == sum(assignmentWithSubmissionWithMarks.usedgrades_task) / 2.0 - - -def test_average_notebook_score_empty(assignment): - assert assignment.average_notebook_score('p1', 'foo') == 0.0 - assert assignment.average_notebook_code_score('p1', 'foo') == 0.0 - assert assignment.average_notebook_written_score('p1', 'foo') == 0.0 - assert assignment.average_notebook_task_score('p1', 'foo') == 0.0 - - -def test_average_notebook_no_score(assignmentWithSubmissionNoMarks): - assert assignmentWithSubmissionNoMarks.average_notebook_score('p1', 'foo') == 0.0 - assert assignmentWithSubmissionNoMarks.average_notebook_code_score('p1', 'foo') == 0.0 - assert assignmentWithSubmissionNoMarks.average_notebook_written_score('p1', 'foo') == 0.0 - assert assignmentWithSubmissionNoMarks.average_notebook_task_score('p1', 'foo') == 0.0 - - -def test_average_notebook_with_score(assignmentWithSubmissionWithMarks: Gradebook) -> None: - assert assignmentWithSubmissionWithMarks.average_notebook_score('p1', 'foo') == sum(assignmentWithSubmissionWithMarks.usedgrades) / 2.0 - assert assignmentWithSubmissionWithMarks.average_notebook_code_score('p1', 'foo') == sum(assignmentWithSubmissionWithMarks.usedgrades_code) / 2.0 - assert assignmentWithSubmissionWithMarks.average_notebook_written_score('p1', 'foo') == sum(assignmentWithSubmissionWithMarks.usedgrades_written) / 2.0 - assert assignmentWithSubmissionWithMarks.average_notebook_task_score('p1', 'foo') == sum(assignmentWithSubmissionWithMarks.usedgrades_task) / 2.0 - - -def test_student_dicts(assignmentWithSubmissionWithMarks): - assign = assignmentWithSubmissionWithMarks - students = assign.student_dicts() - a = sorted(students, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in assign.students], key=lambda x: x["id"]) - assert a == b - - -def test_notebook_max_score(assignmentManyStudents): - assign = assignmentManyStudents - notebook = assign.find_notebook("p1", "foo") - assert notebook.max_score == 44 - - -def test_notebook_max_score_multiple_notebooks(FiveNotebooks): - assign = FiveNotebooks - notebook = assign.find_notebook("n1", "a1") - assert notebook.max_score == 555 - - -def test_submission_max_score(assignmentManyStudents): - assign = assignmentManyStudents - s = assign.find_submission('foo', 's1') - assert s.max_score == 88 - for n in s.notebooks: - assert n.max_score == 44 - - -def test_submission_max_score_multiple_notebooks(FiveNotebooks): - assign = FiveNotebooks - s = assign.find_submission('a1', 's1') - assert s.max_score == 5 * 555 - for n in s.notebooks: - assert n.max_score == 555 - - -def test_notebook_submission_dicts_multiple_students(FiveStudents): - assign = FiveStudents - notebook = assign.find_notebook("n1", "a1") - submissions = assign.notebook_submission_dicts("n1", "a1") - a = sorted(submissions, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in notebook.submissions], key=lambda x: x["id"]) - assert a == b - - -def test_notebook_submission_dicts_multiple_notebooks(FiveNotebooks): - assign = FiveNotebooks - notebook = assign.find_notebook("n1", "a1") - submissions = assign.notebook_submission_dicts("n1", "a1") - a = sorted(submissions, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in notebook.submissions], key=lambda x: x["id"]) - assert a == b - - -def test_notebook_submission_dicts_multiple_assignments(FiveAssignments): - assign = FiveAssignments - notebook = assign.find_notebook("n1", "a1") - submissions = assign.notebook_submission_dicts("n1", "a1") - a = sorted(submissions, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in notebook.submissions], key=lambda x: x["id"]) - assert a == b - - -def test_notebook_submission_dicts(assignmentWithSubmissionWithMarks): - assign = assignmentWithSubmissionWithMarks - notebook = assign.find_notebook("p1", "foo") - submissions = assign.notebook_submission_dicts("p1", "foo") - a = sorted(submissions, key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in notebook.submissions], key=lambda x: x["id"]) - assert a == b - - -def test_submission_dicts_multiple_students(FiveStudents): - assign = FiveStudents - a = sorted(assign.submission_dicts("a1"), key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in assign.find_assignment("a1").submissions], key=lambda x: x["id"]) - assert a == b - - -def test_submission_dicts_multiple_notebooks(FiveNotebooks): - assign = FiveNotebooks - a = sorted(assign.submission_dicts("a1"), key=lambda x: x["id"]) - b = sorted([x.to_dict() for x in assign.find_assignment("a1").submissions], key=lambda x: x["id"]) - assert a == b diff --git a/nbgrader/tests/api/test_models.py b/nbgrader/tests/api/test_models.py deleted file mode 100644 index a38e0f53c..000000000 --- a/nbgrader/tests/api/test_models.py +++ /dev/null @@ -1,1278 +0,0 @@ -import datetime -import pytest -import json - -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker, scoped_session -from sqlalchemy.sql import and_ - -from ... import api - - -@pytest.fixture -def db(request): - engine = create_engine("sqlite:///:memory:") - db = scoped_session(sessionmaker(autoflush=True, bind=engine)) - api.Base.query = db.query_property() - api.Base.metadata.create_all(bind=engine) - - def fin(): - db.remove() - engine.dispose() - request.addfinalizer(fin) - - return db - - -@pytest.fixture -def submissions(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - gc1 = api.GradeCell(name='foo', max_score=10, notebook=n, cell_type="markdown") - gc2 = api.GradeCell(name='bar', max_score=5, notebook=n, cell_type="code") - sc = api.SolutionCell(name='foo', notebook=n) - api.SourceCell( - name='foo', cell_type='markdown', notebook=n, - source='waoiefjwoweifjw', checksum='12345', locked=True) - api.SourceCell( - name='bar', cell_type='code', notebook=n, - source='afejfwejfwe', checksum='567890', locked=False) - db.add(a) - db.commit() - - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere', lms_user_id='230') - sa = api.SubmittedAssignment(assignment=a, student=s) - sn = api.SubmittedNotebook(assignment=sa, notebook=n) - g1a = api.Grade(cell=gc1, notebook=sn) - g2a = api.Grade(cell=gc2, notebook=sn) - ca = api.Comment(cell=sc, notebook=sn) - - db.add(s) - db.commit() - - s = api.Student(id="6789", first_name='John', last_name='Doe', email='johndoe@nowhere', lms_user_id='230') - sa = api.SubmittedAssignment(assignment=a, student=s) - sn = api.SubmittedNotebook(assignment=sa, notebook=n) - g1b = api.Grade(cell=gc1, notebook=sn) - g2b = api.Grade(cell=gc2, notebook=sn) - cb = api.Comment(cell=sc, notebook=sn) - - db.add(s) - db.commit() - - return db, (g1a, g2a, g1b, g2b), (ca, cb) - - -def test_create_assignment(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - db.add(a) - db.commit() - - assert a.id - assert a.name == 'foo' - assert a.duedate == now - assert a.notebooks == [] - assert a.submissions == [] - - assert a.max_score == 0 - assert a.max_code_score == 0 - assert a.max_written_score == 0 - assert a.num_submissions == 0 - - assert repr(a) == "Assignment" - - -def test_create_notebook(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - db.add(a) - db.commit() - - assert n.id - assert n.name == 'blah' - assert n.assignment == a - assert n.grade_cells == [] - assert n.solution_cells == [] - assert n.source_cells == [] - assert n.submissions == [] - assert a.notebooks == [n] - - assert n.max_score == 0 - assert n.max_code_score == 0 - assert n.max_written_score == 0 - - assert repr(n) == "Notebook" - - -def test_create_grade_cell(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - g = api.GradeCell(name='foo', max_score=10, notebook=n, cell_type="code") - db.add(a) - db.commit() - - assert g.id - assert g.name == 'foo' - assert g.max_score == 10 - assert g.cell_type == "code" - assert g.assignment == a - assert g.notebook == n - assert g.grades == [] - assert n.grade_cells == [g] - - assert n.max_score == 10 - assert n.max_code_score == 10 - assert n.max_written_score == 0 - - assert repr(g) == "GradeCell" - - -def test_create_solution_cell(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - s = api.SolutionCell(name='foo', notebook=n) - db.add(a) - db.commit() - - assert s.id - assert s.name == 'foo' - assert s.assignment == a - assert s.notebook == n - assert s.comments == [] - assert n.solution_cells == [s] - - assert repr(s) == "SolutionCell" - - -def test_create_source_cell(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - s = api.SourceCell( - name='foo', notebook=n, source="hello", - cell_type="code", checksum="12345") - db.add(a) - db.commit() - - assert s.id - assert s.name == 'foo' - assert not s.locked - assert s.cell_type == "code" - assert s.source == "hello" - assert s.checksum == "12345" - assert s.assignment == a - assert s.notebook == n - assert n.source_cells == [s] - - assert repr(s) == "SourceCell" - - -def test_create_student(db): - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - db.add(s) - db.commit() - - assert s.id == "12345" - assert s.first_name == 'Jane' - assert s.last_name == 'Doe' - assert s.email == 'janedoe@nowhere' - assert s.submissions == [] - - assert s.score == 0 - assert s.max_score == 0 - - assert repr(s) == "Student<12345>" - - -def test_create_submitted_assignment(db): - a = api.Assignment(name='foo') - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s) - db.add(sa) - db.commit() - - assert sa.id - assert sa.assignment == a - assert sa.student == s - assert sa.notebooks == [] - assert s.submissions == [sa] - assert a.submissions == [sa] - - assert sa.score == 0 - assert sa.max_score == 0 - assert sa.code_score == 0 - assert sa.max_code_score == 0 - assert sa.written_score == 0 - assert sa.max_written_score == 0 - assert not sa.needs_manual_grade - - assert sa.duedate is None - assert sa.timestamp is None - assert sa.extension is None - assert sa.total_seconds_late == 0 - - d = sa.to_dict() - assert d['id'] == sa.id - assert d['name'] == 'foo' - assert d['student'] == '12345' - assert d['timestamp'] == None - assert d['score'] == 0 - assert d['max_score'] == 0 - assert d['code_score'] == 0 - assert d['max_code_score'] == 0 - assert d['written_score'] == 0 - assert d['max_written_score'] == 0 - assert not d['needs_manual_grade'] - - assert repr(sa) == "SubmittedAssignment" - - -def test_submission_timestamp_ontime(db): - duedate = datetime.datetime.utcnow() - timestamp = duedate - datetime.timedelta(days=2) - - a = api.Assignment(name='foo', duedate=duedate) - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s, timestamp=timestamp) - db.add(sa) - db.commit() - - assert sa.duedate == duedate - assert sa.timestamp == timestamp - assert sa.extension is None - assert sa.total_seconds_late == 0 - - -def test_submission_timestamp_late(db): - duedate = datetime.datetime.utcnow() - timestamp = duedate + datetime.timedelta(days=2) - - a = api.Assignment(name='foo', duedate=duedate) - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s, timestamp=timestamp) - db.add(sa) - db.commit() - - assert sa.duedate == duedate - assert sa.timestamp == timestamp - assert sa.extension is None - assert sa.total_seconds_late == 172800 - - -def test_submission_timestamp_with_extension(db): - duedate = datetime.datetime.utcnow() - timestamp = duedate + datetime.timedelta(days=2) - extension = datetime.timedelta(days=3) - - a = api.Assignment(name='foo', duedate=duedate) - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s, timestamp=timestamp, extension=extension) - db.add(sa) - db.commit() - - assert sa.duedate == (duedate + extension) - assert sa.timestamp == timestamp - assert sa.extension == extension - assert sa.total_seconds_late == 0 - - -def test_submission_timestamp_late_with_extension(db): - duedate = datetime.datetime.utcnow() - timestamp = duedate + datetime.timedelta(days=5) - extension = datetime.timedelta(days=3) - - a = api.Assignment(name='foo', duedate=duedate) - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s, timestamp=timestamp, extension=extension) - db.add(sa) - db.commit() - - assert sa.duedate == (duedate + extension) - assert sa.timestamp == timestamp - assert sa.extension == extension - assert sa.total_seconds_late == 172800 - - -def test_create_submitted_notebook(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n1 = api.Notebook(name='blah', assignment=a) - n2 = api.Notebook(name='blah2', assignment=a) - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s) - sn1 = api.SubmittedNotebook(assignment=sa, notebook=n1, late_submission_penalty=5) - sn2 = api.SubmittedNotebook(assignment=sa, notebook=n2, late_submission_penalty=1) - db.add(sn1) - db.add(sn2) - db.commit() - - assert sn1.id - assert sn1.notebook == n1 - assert sn1.assignment == sa - assert sn1.grades == [] - assert sn1.comments == [] - assert sn1.student == s - assert sa.notebooks == [sn1, sn2] - assert n1.submissions == [sn1] - - assert sn1.score == 0 - assert sn1.max_score == 0 - assert sn1.code_score == 0 - assert sn1.max_code_score == 0 - assert sn1.written_score == 0 - assert sn1.max_written_score == 0 - assert sn1.late_submission_penalty == 5 - assert sn2.late_submission_penalty == 1 - assert sa.late_submission_penalty == 6 - assert not sn1.needs_manual_grade - - assert repr(sn1) == "SubmittedNotebook" - - -def test_create_code_grade(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - gc = api.GradeCell(name='foo', max_score=10, notebook=n, cell_type="code") - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s) - sn = api.SubmittedNotebook(assignment=sa, notebook=n) - g = api.Grade(cell=gc, notebook=sn, auto_score=5) - db.add(g) - db.commit() - - assert g.id - assert g.cell == gc - assert g.notebook == sn - assert g.auto_score == 5 - assert g.manual_score is None - assert g.assignment == sa - assert g.student == s - assert g.max_score == 10 - - assert g.needs_manual_grade - assert sn.needs_manual_grade - assert sa.needs_manual_grade - - assert g.score == 5 - assert sn.score == 5 - assert sn.code_score == 5 - assert sn.written_score == 0 - assert sa.score == 5 - assert sa.code_score == 5 - assert sa.written_score == 0 - assert s.score == 5 - - g.manual_score = 7.5 - db.commit() - - assert g.needs_manual_grade - assert sn.needs_manual_grade - assert sa.needs_manual_grade - - assert g.score == 7.5 - assert sn.score == 7.5 - assert sn.code_score == 7.5 - assert sn.written_score == 0 - assert sa.score == 7.5 - assert sa.code_score == 7.5 - assert sa.written_score == 0 - assert s.score == 7.5 - - g.needs_manual_grade = False - db.commit() - - assert not g.needs_manual_grade - assert not sn.needs_manual_grade - assert not sa.needs_manual_grade - - assert repr(g) == "Grade" - - -def test_create_written_grade(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - gc = api.GradeCell(name='foo', max_score=10, notebook=n, cell_type="markdown") - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s) - sn = api.SubmittedNotebook(assignment=sa, notebook=n) - g = api.Grade(cell=gc, notebook=sn) - db.add(g) - db.commit() - - assert g.id - assert g.cell == gc - assert g.notebook == sn - assert g.auto_score is None - assert g.manual_score is None - assert g.assignment == sa - assert g.student == s - assert g.max_score == 10 - - assert g.needs_manual_grade - assert sn.needs_manual_grade - assert sa.needs_manual_grade - - assert g.score == 0 - assert sn.score == 0 - assert sn.code_score == 0 - assert sn.written_score == 0 - assert sa.score == 0 - assert sa.code_score == 0 - assert sa.written_score == 0 - assert s.score == 0 - - g.manual_score = 7.5 - db.commit() - - assert g.needs_manual_grade - assert sn.needs_manual_grade - assert sa.needs_manual_grade - - assert g.score == 7.5 - assert sn.score == 7.5 - assert sn.code_score == 0 - assert sn.written_score == 7.5 - assert sa.score == 7.5 - assert sa.code_score == 0 - assert sa.written_score == 7.5 - assert s.score == 7.5 - - g.needs_manual_grade = False - db.commit() - - assert not g.needs_manual_grade - assert not sn.needs_manual_grade - assert not sa.needs_manual_grade - - assert repr(g) == "Grade" - - -def test_create_comment(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - sc = api.SolutionCell(name='foo', notebook=n) - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - sa = api.SubmittedAssignment(assignment=a, student=s) - sn = api.SubmittedNotebook(assignment=sa, notebook=n) - c = api.Comment(cell=sc, notebook=sn, auto_comment="something") - db.add(c) - db.commit() - - assert c.id - assert c.cell == sc - assert c.notebook == sn - assert c.comment == "something" - assert c.assignment == sa - assert c.student == s - - assert repr(c) == "Comment" - - -def test_query_needs_manual_grade_ungraded(submissions): - db = submissions[0] - - # do all the cells need grading? - a = db.query(api.Grade)\ - .filter(api.Grade.needs_manual_grade)\ - .order_by(api.Grade.id)\ - .all() - b = db.query(api.Grade)\ - .order_by(api.Grade.id)\ - .all() - assert a == b - - # do all the submitted notebooks need grading? - a = db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - b = db.query(api.SubmittedNotebook)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - assert a == b - - # do all the notebooks need grading? - a = db.query(api.Notebook)\ - .filter(api.Notebook.needs_manual_grade)\ - .order_by(api.Notebook.id)\ - .all() - b = db.query(api.Notebook)\ - .order_by(api.Notebook.id)\ - .all() - assert a == b - - # do all the assignments need grading? - a = db.query(api.SubmittedAssignment)\ - .join(api.SubmittedNotebook).join(api.Grade)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .order_by(api.SubmittedAssignment.id)\ - .all() - b = db.query(api.SubmittedAssignment)\ - .order_by(api.SubmittedAssignment.id)\ - .all() - assert a == b - - -def test_query_needs_manual_grade_autograded(submissions): - db, grades, _ = submissions - - for grade in grades: - grade.auto_score = grade.max_score - db.commit() - - # do all the cells need grading? - a = db.query(api.Grade)\ - .filter(api.Grade.needs_manual_grade)\ - .order_by(api.Grade.id)\ - .all() - b = db.query(api.Grade)\ - .order_by(api.Grade.id)\ - .all() - assert a == b - - # do all the submitted notebooks need grading? - a = db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - b = db.query(api.SubmittedNotebook)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - assert a == b - - # do all the notebooks need grading? - a = db.query(api.Notebook)\ - .filter(api.Notebook.needs_manual_grade)\ - .order_by(api.Notebook.id)\ - .all() - b = db.query(api.Notebook)\ - .order_by(api.Notebook.id)\ - .all() - assert a == b - - # do all the assignments need grading? - a = db.query(api.SubmittedAssignment)\ - .join(api.SubmittedNotebook).join(api.Grade)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .order_by(api.SubmittedAssignment.id)\ - .all() - b = db.query(api.SubmittedAssignment)\ - .order_by(api.SubmittedAssignment.id)\ - .all() - assert a == b - - for grade in grades: - grade.needs_manual_grade = False - db.commit() - - # do none of the cells need grading? - assert [] == db.query(api.Grade)\ - .filter(api.Grade.needs_manual_grade)\ - .all() - - # do none of the submitted notebooks need grading? - assert [] == db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .all() - - # do none of the notebooks need grading? - assert [] == db.query(api.Notebook)\ - .filter(api.Notebook.needs_manual_grade)\ - .all() - - # do none of the assignments need grading? - assert [] == db.query(api.SubmittedAssignment)\ - .join(api.SubmittedNotebook).join(api.Grade)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .all() - - -def test_query_needs_manual_grade_manualgraded(submissions): - db, grades, _ = submissions - - for grade in grades: - grade.auto_score = None - grade.manual_score = grade.max_score / 2.0 - db.commit() - - # do all the cells need grading? - a = db.query(api.Grade)\ - .filter(api.Grade.needs_manual_grade)\ - .order_by(api.Grade.id)\ - .all() - b = db.query(api.Grade)\ - .order_by(api.Grade.id)\ - .all() - assert a == b - - # do all the submitted notebooks need grading? - a = db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - b = db.query(api.SubmittedNotebook)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - assert a == b - - # do all the notebooks need grading? - a = db.query(api.Notebook)\ - .filter(api.Notebook.needs_manual_grade)\ - .order_by(api.Notebook.id)\ - .all() - b = db.query(api.Notebook)\ - .order_by(api.Notebook.id)\ - .all() - assert a == b - - # do all the assignments need grading? - a = db.query(api.SubmittedAssignment)\ - .join(api.SubmittedNotebook).join(api.Grade)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .order_by(api.SubmittedAssignment.id)\ - .all() - b = db.query(api.SubmittedAssignment)\ - .order_by(api.SubmittedAssignment.id)\ - .all() - assert a == b - - for grade in grades: - grade.needs_manual_grade = False - db.commit() - - # do none of the cells need grading? - assert [] == db.query(api.Grade)\ - .filter(api.Grade.needs_manual_grade)\ - .all() - - # do none of the submitted notebooks need grading? - assert [] == db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .all() - - # do none of the notebooks need grading? - assert [] == db.query(api.Notebook)\ - .filter(api.Notebook.needs_manual_grade)\ - .all() - - # do none of the assignments need grading? - assert [] == db.query(api.SubmittedAssignment)\ - .join(api.SubmittedNotebook).join(api.Grade)\ - .filter(api.SubmittedNotebook.needs_manual_grade)\ - .all() - - -def test_query_max_score(submissions): - db = submissions[0] - - assert [5, 10] == sorted([x[1] for x in db.query( - api.GradeCell.id, api.GradeCell.max_score).group_by(api.GradeCell.id).all()]) - assert [5, 5, 10, 10] == sorted([x[1] for x in db.query( - api.Grade.id, api.Grade.max_score).group_by(api.Grade.id).all()]) - assert [15] == sorted([x[1] for x in db.query( - api.Notebook.id, api.Notebook.max_score).group_by(api.Notebook.id).all()]) - assert [15, 15] == sorted([x[1] for x in db.query( - api.SubmittedNotebook.id, api.SubmittedNotebook.max_score).group_by(api.SubmittedNotebook.id).all()]) - assert [15] == sorted([x[1] for x in db.query( - api.Assignment.id, api.Assignment.max_score).group_by(api.Assignment.id).all()]) - assert [15, 15] == sorted([x[1] for x in db.query( - api.SubmittedAssignment.id, api.SubmittedAssignment.max_score).group_by(api.SubmittedAssignment.id).all()]) - assert [15, 15] == sorted([x[1] for x in db.query - (api.Student.id, api.Student.max_score).group_by(api.Student.id).all()]) - - -def test_query_score_ungraded(submissions): - db = submissions[0] - - assert [x[0] for x in db.query(api.Grade.score).all()] == [0.0, 0.0, 0.0, 0.0] - assert [x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.score).all()] == [0.0, 0.0] - assert [x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.score).all()] == [0.0, 0.0] - assert [x[1] for x in db.query(api.Student.id, api.Student.score).all()] == [0.0, 0.0] - - -def test_query_comment_unchanged(submissions): - db = submissions[0] - - assert [x[0] for x in db.query(api.Comment.comment).all()] == [None, None] - - -def test_query_score_autograded(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - db.commit() - - assert sorted(x[0] for x in db.query(api.Grade.score).all()) == [0, 2.5, 5, 10] - assert sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.score).all()) == [7.5, 10] - assert sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.score).all()) == [7.5, 10] - assert sorted(x[1] for x in db.query(api.Student.id, api.Student.score).all()) == [7.5, 10] - - -def test_query_auto_comment(submissions): - db, _, comments = submissions - - comments[0].auto_comment = "foo" - comments[1].auto_comment = "bar" - db.commit() - - assert sorted(x[0] for x in db.query(api.Comment.comment).all()) == ["bar", "foo"] - - -def test_query_score_manualgraded(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - grades[0].manual_score = 4 - grades[1].manual_score = 1.5 - grades[2].manual_score = 9 - grades[3].manual_score = 3 - db.commit() - - assert sorted(x[0] for x in db.query(api.Grade.score).all()) == [1.5, 3, 4, 9] - assert sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.score).all()) == [5.5, 12] - assert sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.score).all()) == [5.5, 12] - assert sorted(x[1] for x in db.query(api.Student.id, api.Student.score).all()) == [5.5, 12] - - -def test_query_manual_comment(submissions): - db, _, comments = submissions - - comments[0].auto_comment = "foo" - comments[1].auto_comment = "bar" - comments[0].manual_comment = "baz" - comments[1].manual_comment = "quux" - db.commit() - - assert sorted(x[0] for x in db.query(api.Comment.comment).all()) == ["baz", "quux"] - - -def test_query_max_written_score(submissions): - db = submissions[0] - - assert [10] == sorted([x[1] for x in db.query(api.Notebook.id, api.Notebook.max_written_score).all()]) - assert [10, 10] == sorted([x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.max_written_score).all()]) - assert [10] == sorted([x[1] for x in db.query(api.Assignment.id, api.Assignment.max_written_score).all()]) - assert [10, 10] == sorted([x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.max_written_score).all()]) - - -def test_query_written_score_ungraded(submissions): - db = submissions[0] - - assert [0.0, 0.0] == [x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.written_score).all()] - assert [0.0, 0.0] == [x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.written_score).all()] - - -def test_query_written_score_autograded(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - db.commit() - - assert [5, 10] == sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.written_score).all()) - assert [5, 10] == sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.written_score).all()) - - -def test_query_written_score_manualgraded(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - grades[0].manual_score = 4 - grades[1].manual_score = 1.5 - grades[2].manual_score = 9 - grades[3].manual_score = 3 - db.commit() - - assert [4, 9] == sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.written_score).all()) - assert [4, 9] == sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.written_score).all()) - - -def test_query_max_code_score(submissions): - db = submissions[0] - - assert [5] == sorted([x[1] for x in db.query(api.Notebook.id, api.Notebook.max_code_score).all()]) - assert [5, 5] == sorted([x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.max_code_score).all()]) - assert [5] == sorted([x[1] for x in db.query(api.Assignment.id, api.Assignment.max_code_score).all()]) - assert [5, 5] == sorted([x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.max_code_score).all()]) - - -def test_query_code_score_ungraded(submissions): - db = submissions[0] - - assert [0.0, 0.0] == [x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.code_score).all()] - assert [0.0, 0.0] == [x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.code_score).all()] - - -def test_query_code_score_autograded(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - db.commit() - - assert [0, 2.5] == sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.code_score).all()) - assert [0, 2.5] == sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.code_score).all()) - - -def test_query_code_score_manualgraded(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - grades[0].manual_score = 4 - grades[1].manual_score = 1.5 - grades[2].manual_score = 9 - grades[3].manual_score = 3 - db.commit() - - assert [1.5, 3] == sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.code_score).all()) - assert [1.5, 3] == sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.code_score).all()) - - -def test_query_auto_score_extra_credit(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - - grades[0].extra_credit = 0.5 - grades[1].extra_credit = 0 - grades[2].extra_credit = 2.3 - grades[3].extra_credit = 1.1 - db.commit() - - assert sorted(x[0] for x in db.query(api.Grade.score).all()) == [0, 3.6, 7.3, 10.5] - assert sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.score).all()) == [10.5, 10.9] - assert sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.score).all()) == [10.5, 10.9] - assert sorted(x[1] for x in db.query(api.Student.id, api.Student.score).all()) == [10.5, 10.9] - - -def test_query_manual_score_extra_credit(submissions): - db, grades, _ = submissions - - grades[0].auto_score = 10 - grades[1].auto_score = 0 - grades[2].auto_score = 5 - grades[3].auto_score = 2.5 - - grades[0].manual_score = 4 - grades[1].manual_score = 1.5 - grades[2].manual_score = 9 - grades[3].manual_score = 3 - - grades[0].extra_credit = 0.5 - grades[1].extra_credit = 0 - grades[2].extra_credit = 2.3 - grades[3].extra_credit = 1.1 - db.commit() - - assert sorted(x[0] for x in db.query(api.Grade.score).all()) == [1.5, 4.1, 4.5, 11.3] - assert sorted(x[1] for x in db.query(api.SubmittedNotebook.id, api.SubmittedNotebook.score).all()) == [6, 15.4] - assert sorted(x[1] for x in db.query(api.SubmittedAssignment.id, api.SubmittedAssignment.score).all()) == [6, 15.4] - assert sorted(x[1] for x in db.query(api.Student.id, api.Student.score).all()) == [6, 15.4] - - -def test_query_num_submissions(submissions): - db = submissions[0] - - assert [2] == [x[0] for x in db.query(api.Assignment.num_submissions).all()] - assert [2] == [x[0] for x in db.query(api.Notebook.num_submissions).all()] - - -def test_student_max_score(db): - now = datetime.datetime.utcnow() - a = api.Assignment(name='foo', duedate=now) - n = api.Notebook(name='blah', assignment=a) - api.GradeCell(name='foo', max_score=10, notebook=n, cell_type="markdown") - api.GradeCell(name='bar', max_score=5, notebook=n, cell_type="code") - db.add(a) - db.commit() - - s = api.Student(id="12345", first_name='Jane', last_name='Doe', email='janedoe@nowhere') - db.add(s) - db.commit() - - assert s.max_score == 15 - - -def test_query_grade_cell_types(submissions): - db = submissions[0] - - a = db.query(api.Grade)\ - .filter(api.Grade.cell_type == "code")\ - .order_by(api.Grade.id)\ - .all() - b = db.query(api.Grade)\ - .join(api.GradeCell)\ - .filter(api.GradeCell.cell_type == "code")\ - .order_by(api.Grade.id)\ - .all() - assert a == b - - a = db.query(api.Grade)\ - .filter(api.Grade.cell_type == "markdown")\ - .order_by(api.Grade.id)\ - .all() - b = db.query(api.Grade)\ - .join(api.GradeCell)\ - .filter(api.GradeCell.cell_type == "markdown")\ - .order_by(api.Grade.id)\ - .all() - assert a == b - - -def test_query_failed_tests_failed(submissions): - db, grades, _ = submissions - - for grade in grades: - if grade.cell.cell_type == "code": - grade.auto_score = 0 - db.commit() - - # have all the cells failed? - a = db.query(api.Grade)\ - .filter(api.Grade.failed_tests)\ - .order_by(api.Grade.id)\ - .all() - b = db.query(api.Grade)\ - .filter(api.Grade.cell_type == "code")\ - .order_by(api.Grade.id)\ - .all() - assert a == b - - # have all the notebooks failed? - a = db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.failed_tests)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - b = db.query(api.SubmittedNotebook)\ - .order_by(api.SubmittedNotebook.id)\ - .all() - - -def test_query_failed_tests_ok(submissions): - db, all_grades, _ = submissions - - for grade in all_grades: - if grade.cell.cell_type == "code": - grade.auto_score = grade.max_score - db.commit() - - # are all the grades ok? - assert [] == db.query(api.Grade)\ - .filter(api.Grade.failed_tests)\ - .all() - - # are all the notebooks ok? - assert [] == db.query(api.SubmittedNotebook)\ - .filter(api.SubmittedNotebook.failed_tests)\ - .all() - - -def test_assignment_to_dict(submissions): - db = submissions[0] - - a = db.query(api.Assignment).one() - ad = a.to_dict() - - assert set(ad.keys()) == { - 'id', 'name', 'duedate', 'num_submissions', 'max_score', - 'max_code_score', 'max_written_score', 'max_task_score'} - - assert ad['id'] == a.id - assert ad['name'] == "foo" - assert ad['duedate'] == a.duedate.isoformat() - assert ad['num_submissions'] == 2 - assert ad['max_score'] == 15 - assert ad['max_code_score'] == 5 - assert ad['max_written_score'] == 10 - - # make sure it can be JSONified - json.dumps(ad) - - -def test_notebook_to_dict(submissions): - db = submissions[0] - - a = db.query(api.Assignment).one() - n, = a.notebooks - nd = n.to_dict() - - assert set(nd.keys()) == { - 'id', 'name', 'num_submissions', 'max_score', 'max_code_score', - 'max_written_score', 'needs_manual_grade', 'max_task_score'} - - assert nd['id'] == n.id - assert nd['name'] == 'blah' - assert nd['num_submissions'] == 2 - assert nd['max_score'] == 15 - assert nd['max_code_score'] == 5 - assert nd['max_written_score'] == 10 - assert nd['needs_manual_grade'] - - # make sure it can be JSONified - json.dumps(nd) - - -def test_gradecell_to_dict(submissions): - db = submissions[0] - - gc1 = db.query(api.GradeCell).filter(api.GradeCell.name == 'foo').one() - gc2 = db.query(api.GradeCell).filter(api.GradeCell.name == 'bar').one() - - gc1d = gc1.to_dict() - gc2d = gc2.to_dict() - - assert set(gc1d.keys()) == set(gc2d.keys()) - assert set(gc1d.keys()) == { - 'id', 'name', 'max_score', 'cell_type', 'notebook', 'assignment'} - - assert gc1d['id'] == gc1.id - assert gc1d['name'] == 'foo' - assert gc1d['max_score'] == 10 - assert gc1d['cell_type'] == 'markdown' - assert gc1d['notebook'] == 'blah' - assert gc1d['assignment'] == 'foo' - - assert gc2d['id'] == gc2.id - assert gc2d['name'] == 'bar' - assert gc2d['max_score'] == 5 - assert gc2d['cell_type'] == 'code' - assert gc2d['notebook'] == 'blah' - assert gc2d['assignment'] == 'foo' - - # make sure it can be JSONified - json.dumps(gc1d) - json.dumps(gc2d) - - -def test_solutioncell_to_dict(submissions): - db = submissions[0] - - sc = db.query(api.SolutionCell).one() - scd = sc.to_dict() - - assert set(scd.keys()) == {'id', 'name', 'notebook', 'assignment'} - - assert scd['id'] == sc.id - assert scd['name'] == 'foo' - assert scd['notebook'] == 'blah' - assert scd['assignment'] == 'foo' - - # make sure it can be JSONified - json.dumps(scd) - - -def test_sourcecell_to_dict(submissions): - db = submissions[0] - - sc1 = db.query(api.SourceCell).filter(api.SourceCell.name == 'foo').one() - sc2 = db.query(api.SourceCell).filter(api.SourceCell.name == 'bar').one() - - sc1d = sc1.to_dict() - sc2d = sc2.to_dict() - - assert set(sc1d.keys()) == set(sc2d.keys()) - assert set(sc1d.keys()) == { - 'id', 'name', 'cell_type', 'source', 'checksum', 'locked', - 'notebook', 'assignment'} - - assert sc1d['id'] == sc1.id - assert sc1d['name'] == 'foo' - assert sc1d['cell_type'] == 'markdown' - assert sc1d['source'] == 'waoiefjwoweifjw' - assert sc1d['checksum'] == '12345' - assert sc1d['notebook'] == 'blah' - assert sc1d['assignment'] == 'foo' - assert sc1d['locked'] - - assert sc2d['id'] == sc2.id - assert sc2d['name'] == 'bar' - assert sc2d['cell_type'] == 'code' - assert sc2d['source'] == 'afejfwejfwe' - assert sc2d['checksum'] == '567890' - assert sc2d['notebook'] == 'blah' - assert sc2d['assignment'] == 'foo' - assert not sc2d['locked'] - - # make sure it can be JSONified - json.dumps(sc1d) - json.dumps(sc2d) - - -def test_student_to_dict(submissions): - db = submissions[0] - - s1 = db.query(api.Student).filter(api.Student.id == '12345').one() - s2 = db.query(api.Student).filter(api.Student.id == '6789').one() - - s1d = s1.to_dict() - s2d = s2.to_dict() - - assert set(s1d.keys()) == set(s2d.keys()) - assert set(s1d.keys()) == { - 'id', 'first_name', 'last_name', 'email', 'score', 'max_score', 'lms_user_id'} - - assert s1d['id'] == '12345' - assert s1d['first_name'] == 'Jane' - assert s1d['last_name'] == 'Doe' - assert s1d['email'] == 'janedoe@nowhere' - assert s1d['score'] == 0 - assert s1d['max_score'] == 15 - assert s1d['lms_user_id'] == '230' - - assert s2d['id'] == '6789' - assert s2d['first_name'] == 'John' - assert s2d['last_name'] == 'Doe' - assert s2d['email'] == 'johndoe@nowhere' - assert s2d['score'] == 0 - assert s2d['max_score'] == 15 - assert s2d['lms_user_id'] == '230' - - # make sure it can be JSONified - json.dumps(s1d) - json.dumps(s2d) - - -def test_submittedassignment_to_dict(submissions): - db = submissions[0] - - sa = db.query(api.SubmittedAssignment)\ - .join(api.Student)\ - .filter(api.Student.id == '12345')\ - .one() - - sad = sa.to_dict() - - assert set(sad.keys()) == { - 'id', 'name', 'student', 'timestamp', 'score', 'max_score', 'code_score', - 'max_code_score', 'written_score', 'max_written_score', - 'task_score', 'max_task_score', - 'needs_manual_grade', 'last_name', 'first_name'} - - assert sad['id'] == sa.id - assert sad['name'] == 'foo' - assert sad['student'] == '12345' - assert sad['last_name'] == 'Doe' - assert sad['first_name'] == 'Jane' - assert sad['timestamp'] is None - assert sad['score'] == 0 - assert sad['max_score'] == 15 - assert sad['code_score'] == 0 - assert sad['max_code_score'] == 5 - assert sad['written_score'] == 0 - assert sad['max_written_score'] == 10 - assert sad['needs_manual_grade'] - - # make sure it can be JSONified - json.dumps(sad) - - -def test_submittednotebook_to_dict(submissions): - db = submissions[0] - - sn = db.query(api.SubmittedNotebook)\ - .join(api.Notebook).join(api.SubmittedAssignment).join(api.Student)\ - .filter(and_( - api.Student.id == '12345', - api.Notebook.name == 'blah'))\ - .one() - - snd = sn.to_dict() - - assert set(snd.keys()) == { - 'id', 'name', 'student', 'last_name', 'first_name', - 'score', 'max_score', 'code_score', - 'max_code_score', 'written_score', 'max_written_score', - 'task_score', 'max_task_score', - 'needs_manual_grade', 'failed_tests', 'flagged'} - - assert snd['id'] == sn.id - assert snd['name'] == 'blah' - assert snd['student'] == '12345' - assert snd['last_name'] == 'Doe' - assert snd['first_name'] == 'Jane' - assert snd['score'] == 0 - assert snd['max_score'] == 15 - assert snd['code_score'] == 0 - assert snd['max_code_score'] == 5 - assert snd['written_score'] == 0 - assert snd['max_written_score'] == 10 - assert snd['needs_manual_grade'] - assert not snd['failed_tests'] - assert not snd['flagged'] - - # make sure it can be JSONified - json.dumps(snd) - - -def test_grade_to_dict(submissions): - _, grades, _ = submissions - - for g in grades: - gd = g.to_dict() - assert set(gd.keys()) == { - 'id', 'name', 'notebook', 'assignment', 'student', 'auto_score', - 'manual_score', 'max_score', 'needs_manual_grade', 'failed_tests', - 'cell_type', 'extra_credit'} - - assert gd['id'] == g.id - assert gd['name'] == g.name - assert gd['notebook'] == 'blah' - assert gd['assignment'] == 'foo' - assert gd['student'] == g.student.id - assert gd['auto_score'] is None - assert gd['manual_score'] is None - assert gd['extra_credit'] is None - assert gd['needs_manual_grade'] - assert not gd['failed_tests'] - assert gd['cell_type'] == g.cell_type - - # make sure it can be JSONified - json.dumps(gd) - - -def test_comment_to_dict(submissions): - _, _, comments = submissions - - for c in comments: - cd = c.to_dict() - assert set(cd.keys()) == { - 'id', 'name', 'notebook', 'assignment', 'student', 'auto_comment', - 'manual_comment'} - - assert cd['id'] == c.id - assert cd['name'] == c.name - assert cd['notebook'] == 'blah' - assert cd['assignment'] == 'foo' - assert cd['student'] == c.student.id - assert cd['auto_comment'] is None - assert cd['manual_comment'] is None - - # make sure it can be JSONified - json.dumps(cd) diff --git a/nbgrader/tests/apps/__init__.py b/nbgrader/tests/apps/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/nbgrader/tests/apps/base.py b/nbgrader/tests/apps/base.py deleted file mode 100644 index 9e5c6b8a2..000000000 --- a/nbgrader/tests/apps/base.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- - -import io -import os -import shutil -import pytest - -from nbformat import write as write_nb -from nbformat.v4 import new_notebook - -from ...utils import remove - - -@pytest.mark.usefixtures("temp_cwd") -class BaseTestApp(object): - - def _empty_notebook(self, path, kernel=None): - nb = new_notebook() - if kernel is not None: - nb.metadata.kernelspec = { - "display_name": "kernel", - "language": kernel, - "name": kernel - } - - full_dest = os.path.abspath(path) - if not os.path.exists(os.path.dirname(full_dest)): - os.makedirs(os.path.dirname(full_dest)) - if os.path.exists(full_dest): - remove(full_dest) - with io.open(full_dest, mode='w', encoding='utf-8') as f: - write_nb(nb, f, 4) - - def _copy_file(self, src: str, dest: str) -> None: - full_src = os.path.join(os.path.dirname(__file__), src) - full_dest = os.path.abspath(dest) - if not os.path.exists(os.path.dirname(full_dest)): - os.makedirs(os.path.dirname(full_dest)) - if os.path.exists(full_dest): - remove(full_dest) - shutil.copy(full_src, full_dest) - - def _move_file(self, src, dest): - full_src = os.path.abspath(src) - full_dest = os.path.abspath(dest) - if not os.path.exists(os.path.dirname(full_dest)): - os.makedirs(os.path.dirname(full_dest)) - if os.path.exists(full_dest): - remove(full_dest) - shutil.move(full_src, full_dest) - - def _make_file(self, path: str, contents: str = "") -> None: - full_dest = os.path.abspath(path) - if not os.path.exists(os.path.dirname(full_dest)): - os.makedirs(os.path.dirname(full_dest)) - if os.path.exists(full_dest): - remove(full_dest) - with open(path, "w") as fh: - fh.write(contents) - - def _get_permissions(self, filename): - st_mode = os.stat(filename).st_mode - # If setgid is true, return four bytes. For testing CourseDirectory.groupshared. - if st_mode & 0o2000: - return oct(st_mode)[-4:] - return oct(st_mode)[-3:] - - def _file_contents(self, path): - with open(path, "r") as fh: - contents = fh.read() - return contents diff --git a/nbgrader/tests/apps/conftest.py b/nbgrader/tests/apps/conftest.py deleted file mode 100644 index e1173224b..000000000 --- a/nbgrader/tests/apps/conftest.py +++ /dev/null @@ -1,134 +0,0 @@ -import os -import tempfile -import shutil -import pytest -import sys - -from textwrap import dedent - -from _pytest.fixtures import SubRequest - -from ...api import Gradebook -from ...utils import rmtree - - -@pytest.fixture -def db(request: SubRequest) -> str: - path = tempfile.mkdtemp(prefix='tmp-dbdir-') - dbpath = os.path.join(path, "nbgrader_test.db") - - def fin() -> None: - rmtree(path) - request.addfinalizer(fin) - - return "sqlite:///" + dbpath - - -@pytest.fixture -def course_dir(request: SubRequest) -> str: - path = tempfile.mkdtemp(prefix='tmp-coursedir-') - - def fin() -> None: - rmtree(path) - request.addfinalizer(fin) - - return path - - -@pytest.fixture -def temp_cwd(request: SubRequest, course_dir: str) -> str: - orig_dir = os.getcwd() - path = tempfile.mkdtemp(prefix='tmp-cwd-') - os.chdir(path) - - with open("nbgrader_config.py", "w") as fh: - fh.write(dedent( - """ - c = get_config() - c.CourseDirectory.root = r"{}" - """.format(course_dir) - )) - - def fin() -> None: - os.chdir(orig_dir) - rmtree(path) - request.addfinalizer(fin) - - return path - - -@pytest.fixture -def jupyter_config_dir(request): - path = tempfile.mkdtemp(prefix='tmp-configdir-') - - def fin(): - rmtree(path) - request.addfinalizer(fin) - - return path - - -@pytest.fixture -def jupyter_data_dir(request): - path = tempfile.mkdtemp(prefix='tmp-datadir-') - - def fin(): - rmtree(path) - request.addfinalizer(fin) - - return path - - -@pytest.fixture -def fake_home_dir(request, monkeypatch): - ''' - this fixture creates a temporary home directory. This prevents existing - nbgrader_config.py files in the user directory to interfer with the tests. - ''' - path = tempfile.mkdtemp(prefix='tmp-homedir-') - - def fin(): - rmtree(path) - request.addfinalizer(fin) - - monkeypatch.setenv('HOME', str(path)) - - return path - - -@pytest.fixture -def env(request, jupyter_config_dir, jupyter_data_dir): - env = os.environ.copy() - env['JUPYTER_DATA_DIR'] = jupyter_data_dir - env['JUPYTER_CONFIG_DIR'] = jupyter_config_dir - return env - - -@pytest.fixture -def exchange(request): - path = tempfile.mkdtemp(prefix='tmp-exchange-') - - def fin(): - rmtree(path) - request.addfinalizer(fin) - - return path - - -@pytest.fixture -def cache(request): - path = tempfile.mkdtemp(prefix='tmp-cache-') - - def fin(): - rmtree(path) - request.addfinalizer(fin) - - return path - -notwindows = pytest.mark.skipif( - sys.platform == 'win32', - reason='This functionality of nbgrader is unsupported on Windows') - -windows = pytest.mark.skipif( - sys.platform != 'win32', - reason='This test is only to be run on Windows') diff --git a/nbgrader/tests/apps/files/__init__.py b/nbgrader/tests/apps/files/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/nbgrader/tests/apps/files/autotest-hashed-changed.ipynb b/nbgrader/tests/apps/files/autotest-hashed-changed.ipynb deleted file mode 100644 index 8ddbb8633..000000000 --- a/nbgrader/tests/apps/files/autotest-hashed-changed.ipynb +++ /dev/null @@ -1,112 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "a0a263f3cd77437ecaaaa68ffd10ca2f", - "grade": true, - "grade_id": "test_hashed", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "from hashlib import sha1\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"9231f34cdc3da9ea\").hexdigest() == \"659113e142e34b819add5d3c95d33a1cf10a09ae\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"9231f34cdc3da9ea\").hexdigest() == \"455510a3efa0017fa35841cd77fb1554ee665d0a\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(b)).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"a7a6d540df4104f3adb629c5c3ea3c7c461eaa6f\", \"type of b is not str. b should be an str\"\n", - "assert sha1(str(len(b)).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"b8eda0d698f28aedc80f0f318c35af3447c18f29\", \"length of b is not correct\"\n", - "assert sha1(str(b.lower()).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"2755a4e47f3b60f679787659f804488276ec718e\", \"value of b is not correct\"\n", - "assert sha1(str(b).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"2755a4e47f3b60f679787659f804488276ec718e\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert sha1(str(type(c)).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"2da3504fe36c50ab1d44ffb61bd4a02af8f0fdbd\", \"type of c is not list. c should be a list\"\n", - "assert sha1(str(len(c)).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"5f8b7c79f0b7ee5fafa410947a40589acbf2b644\", \"length of c is not correct\"\n", - "assert sha1(str(sorted(map(str, c))).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"10c943e426df5e88a394da50cc9cc07f6cb2fa33\", \"values of c are not correct\"\n", - "assert sha1(str(c).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"c538ca013d170994051b3d55fb2c488d2fadb776\", \"order of elements of c is not correct\"\n", - "\n", - "\n", - "# differing spacings, num comment characters, trailing whitespace\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"cfe4dae897378d1a\").hexdigest() == \"463fc87d8b90bcf558e04b5bff454d0fe10f0447\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"cfe4dae897378d1a\").hexdigest() == \"586e477f328cc5e3c852148517751d072d36afee\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"ec2be017656b8d95\").hexdigest() == \"2abb3c781a9ed14b1dec5a00736dc170f3301ce2\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"ec2be017656b8d95\").hexdigest() == \"b86b5db1c86084688de67ed5b3e2001206365a44\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"da663014407e8b68\").hexdigest() == \"4c5e40a4f0ec62a3dbf5232e9d22faba40b1d394\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"da663014407e8b68\").hexdigest() == \"419c839a51a681a8fe1bd658d02c285ab657a1ef\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"f97fab4e29c2f008\").hexdigest() == \"21248e96318cac95a37157dcded2b3e9b3778668\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"f97fab4e29c2f008\").hexdigest() == \"0e27de567679deef32bd63ebd416cb835db76ba4\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"72eea5ecf1bd1e19\").hexdigest() == \"6008755168651743936153fa68a21c1c4369b61a\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"72eea5ecf1bd1e19\").hexdigest() == \"b5d66cbe4aaaaf8a45e1ceef0e2e03f70fcefa59\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"93205c8cd03cafd5\").hexdigest() == \"2292f651355b47a594a97fe2e04253414aefe4bd\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"93205c8cd03cafd5\").hexdigest() == \"f5eba7f0391b94b32a57691d8f982659d50fbf9f\", \"value of a is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-hashed-unchanged.ipynb b/nbgrader/tests/apps/files/autotest-hashed-unchanged.ipynb deleted file mode 100644 index 86f485097..000000000 --- a/nbgrader/tests/apps/files/autotest-hashed-unchanged.ipynb +++ /dev/null @@ -1,110 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "raise NotImplementedError()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "a0a263f3cd77437ecaaaa68ffd10ca2f", - "grade": true, - "grade_id": "test_hashed", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "from hashlib import sha1\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"9231f34cdc3da9ea\").hexdigest() == \"659113e142e34b819add5d3c95d33a1cf10a09ae\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"9231f34cdc3da9ea\").hexdigest() == \"455510a3efa0017fa35841cd77fb1554ee665d0a\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(b)).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"a7a6d540df4104f3adb629c5c3ea3c7c461eaa6f\", \"type of b is not str. b should be an str\"\n", - "assert sha1(str(len(b)).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"b8eda0d698f28aedc80f0f318c35af3447c18f29\", \"length of b is not correct\"\n", - "assert sha1(str(b.lower()).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"2755a4e47f3b60f679787659f804488276ec718e\", \"value of b is not correct\"\n", - "assert sha1(str(b).encode(\"utf-8\")+b\"13937391b113e128\").hexdigest() == \"2755a4e47f3b60f679787659f804488276ec718e\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert sha1(str(type(c)).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"2da3504fe36c50ab1d44ffb61bd4a02af8f0fdbd\", \"type of c is not list. c should be a list\"\n", - "assert sha1(str(len(c)).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"5f8b7c79f0b7ee5fafa410947a40589acbf2b644\", \"length of c is not correct\"\n", - "assert sha1(str(sorted(map(str, c))).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"10c943e426df5e88a394da50cc9cc07f6cb2fa33\", \"values of c are not correct\"\n", - "assert sha1(str(c).encode(\"utf-8\")+b\"e33308cee3b48f12\").hexdigest() == \"c538ca013d170994051b3d55fb2c488d2fadb776\", \"order of elements of c is not correct\"\n", - "\n", - "\n", - "# differing spacings, num comment characters, trailing whitespace\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"cfe4dae897378d1a\").hexdigest() == \"463fc87d8b90bcf558e04b5bff454d0fe10f0447\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"cfe4dae897378d1a\").hexdigest() == \"586e477f328cc5e3c852148517751d072d36afee\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"ec2be017656b8d95\").hexdigest() == \"2abb3c781a9ed14b1dec5a00736dc170f3301ce2\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"ec2be017656b8d95\").hexdigest() == \"b86b5db1c86084688de67ed5b3e2001206365a44\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"da663014407e8b68\").hexdigest() == \"4c5e40a4f0ec62a3dbf5232e9d22faba40b1d394\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"da663014407e8b68\").hexdigest() == \"419c839a51a681a8fe1bd658d02c285ab657a1ef\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"f97fab4e29c2f008\").hexdigest() == \"21248e96318cac95a37157dcded2b3e9b3778668\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"f97fab4e29c2f008\").hexdigest() == \"0e27de567679deef32bd63ebd416cb835db76ba4\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"72eea5ecf1bd1e19\").hexdigest() == \"6008755168651743936153fa68a21c1c4369b61a\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"72eea5ecf1bd1e19\").hexdigest() == \"b5d66cbe4aaaaf8a45e1ceef0e2e03f70fcefa59\", \"value of a is not correct\"\n", - "\n", - "assert sha1(str(type(a)).encode(\"utf-8\")+b\"93205c8cd03cafd5\").hexdigest() == \"2292f651355b47a594a97fe2e04253414aefe4bd\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert sha1(str(a).encode(\"utf-8\")+b\"93205c8cd03cafd5\").hexdigest() == \"f5eba7f0391b94b32a57691d8f982659d50fbf9f\", \"value of a is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-hashed.ipynb b/nbgrader/tests/apps/files/autotest-hashed.ipynb deleted file mode 100644 index 1b4df7b83..000000000 --- a/nbgrader/tests/apps/files/autotest-hashed.ipynb +++ /dev/null @@ -1,82 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "### BEGIN SOLUTION\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]\n", - "### END SOLUTION" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "test_hashed", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "### HASHED AUTOTEST a\n", - "### HASHED AUTOTEST b\n", - "### HASHED AUTOTEST c\n", - "\n", - "# differing spacings, num comment characters, trailing whitespace\n", - "### HASHED AUTOTEST a \n", - "#HASHED AUTOTEST a\n", - "# HASHED AUTOTEST a\n", - "## HASHED AUTOTEST a\n", - "# HASHED AUTOTEST a\n", - "# # # # HASHED AUTOTEST a" - ] - } - ], - "metadata": { - "celltoolbar": "Create Assignment", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-hidden-changed-right.ipynb b/nbgrader/tests/apps/files/autotest-hidden-changed-right.ipynb deleted file mode 100644 index d17fa7735..000000000 --- a/nbgrader/tests/apps/files/autotest-hidden-changed-right.ipynb +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "2fd15c162690346636dd1f2881f6b31e", - "grade": true, - "grade_id": "test_hidden", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "\n", - "assert str(type(a)) == \"\", \"type of type(a) is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of type(b) is not correct\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of type(c) is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-hidden-changed-wrong.ipynb b/nbgrader/tests/apps/files/autotest-hidden-changed-wrong.ipynb deleted file mode 100644 index e84452ef5..000000000 --- a/nbgrader/tests/apps/files/autotest-hidden-changed-wrong.ipynb +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "a = 7\n", - "b = \"notright\"\n", - "c = [3, 4, \"hi\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "2fd15c162690346636dd1f2881f6b31e", - "grade": true, - "grade_id": "test_hidden", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "\n", - "assert str(type(a)) == \"\", \"type of type(a) is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of type(b) is not correct\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of type(c) is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-hidden-unchanged.ipynb b/nbgrader/tests/apps/files/autotest-hidden-unchanged.ipynb deleted file mode 100644 index be153756b..000000000 --- a/nbgrader/tests/apps/files/autotest-hidden-unchanged.ipynb +++ /dev/null @@ -1,83 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "raise NotImplementedError()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "2fd15c162690346636dd1f2881f6b31e", - "grade": true, - "grade_id": "test_hidden", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "\n", - "assert str(type(a)) == \"\", \"type of type(a) is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of type(b) is not correct\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of type(c) is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-hidden.ipynb b/nbgrader/tests/apps/files/autotest-hidden.ipynb deleted file mode 100644 index c2e6e8e23..000000000 --- a/nbgrader/tests/apps/files/autotest-hidden.ipynb +++ /dev/null @@ -1,80 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "### BEGIN SOLUTION\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]\n", - "### END SOLUTION" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "test_hidden", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "### BEGIN HIDDEN TESTS\n", - "### AUTOTEST a\n", - "### AUTOTEST b\n", - "### AUTOTEST c\n", - "### END HIDDEN TESTS\n", - "\n", - "### AUTOTEST type(a)\n", - "### AUTOTEST type(b)\n", - "### AUTOTEST type(c)" - ] - } - ], - "metadata": { - "celltoolbar": "Create Assignment", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-multi-changed.ipynb b/nbgrader/tests/apps/files/autotest-multi-changed.ipynb deleted file mode 100644 index 04470cca4..000000000 --- a/nbgrader/tests/apps/files/autotest-multi-changed.ipynb +++ /dev/null @@ -1,267 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]\n", - "d = {1 : 6, 3: 5} #right type, wrong value\n", - "e = [5, -3.3, 'd'] #right values, wrong type\n", - "f = True # wrong value\n", - "def fun(x):\n", - " if x < 0:\n", - " raise ValueError\n", - " else:\n", - " return x" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "620860a42fa05b556da01013ef99ed8c", - "grade": true, - "grade_id": "test_multi", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "# the basic tests from the simple notebook\n", - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of b is not str. b should be an str\"\n", - "assert str(len(b)) == \"5\", \"length of b is not correct\"\n", - "assert str(b.lower()) == \"hello\", \"value of b is not correct\"\n", - "assert str(b) == \"hello\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of c is not list. c should be a list\"\n", - "assert str(len(c)) == \"3\", \"length of c is not correct\"\n", - "assert str(sorted(map(str, c))) == \"['1', '2', 'test']\", \"values of c are not correct\"\n", - "assert str(c) == \"[1, 2, 'test']\", \"order of elements of c is not correct\"\n", - "\n", - "print('Success!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "c46928757ed303ce204ef97299b39f82", - "grade": true, - "grade_id": "cell-f2803ba7c42d03ab", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "# multiple expressions per line\n", - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of type(a) is not correct\"\n", - "\n", - "assert str(type(str(a))) == \"\", \"type of str(a) is not str. str(a) should be an str\"\n", - "assert str(len(str(a))) == \"1\", \"length of str(a) is not correct\"\n", - "assert str(str(a).lower()) == \"5\", \"value of str(a) is not correct\"\n", - "assert str(str(a)) == \"5\", \"correct string value of str(a) but incorrect case of letters\"\n", - "\n", - "assert str(type(a == \"5\")) == \"\", \"type of a == \\\"5\\\" is not bool. a == \\\"5\\\" should be a bool\"\n", - "assert str(a == \"5\") == \"False\", \"boolean value of a == \\\"5\\\" is not correct\"\n", - "\n", - "assert str(type([ch for ch in b])) == \"\", \"type of [ch for ch in b] is not list. [ch for ch in b] should be a list\"\n", - "assert str(len([ch for ch in b])) == \"5\", \"length of [ch for ch in b] is not correct\"\n", - "assert str(sorted(map(str, [ch for ch in b]))) == \"['e', 'h', 'l', 'l', 'o']\", \"values of [ch for ch in b] are not correct\"\n", - "assert str([ch for ch in b]) == \"['h', 'e', 'l', 'l', 'o']\", \"order of elements of [ch for ch in b] is not correct\"\n", - "\n", - "assert str(type(len(c))) == \"\", \"type of len(c) is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(len(c)) == \"3\", \"value of len(c) is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "\n", - "# intervening regular code\n", - "print(\"hello!\")\n", - "\n", - "# differing spacings, numbers of comment characters, trailing whitespace\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "\n", - "\n", - "print('Success!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "2fc8066fae8e7aeb865116c88d1be31a", - "grade": true, - "grade_id": "cell-693350420ec62f1b", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "# a few common types\n", - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of b is not str. b should be an str\"\n", - "assert str(len(b)) == \"5\", \"length of b is not correct\"\n", - "assert str(b.lower()) == \"hello\", \"value of b is not correct\"\n", - "assert str(b) == \"hello\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of c is not list. c should be a list\"\n", - "assert str(len(c)) == \"3\", \"length of c is not correct\"\n", - "assert str(sorted(map(str, c))) == \"['1', '2', 'test']\", \"values of c are not correct\"\n", - "assert str(c) == \"[1, 2, 'test']\", \"order of elements of c is not correct\"\n", - "\n", - "assert str(type(d)) == \"\", \"type of d is not dict. d should be a dict\"\n", - "assert str(len(list(d.keys()))) == \"2\", \"number of keys of d is not correct\"\n", - "assert str(sorted(map(str, d.keys()))) == \"['3', 'a']\", \"keys of d are not correct\"\n", - "assert str(sorted(map(str, d.values()))) == \"['[1.2, 3]', 'f']\", \"correct keys, but values of d are not correct\"\n", - "assert str(d) == \"{'a': 'f', 3: [1.2, 3]}\", \"correct keys and values, but incorrect correspondence in keys and values of d\"\n", - "\n", - "assert str(type(e)) == \"\", \"type of e is not tuple. e should be a tuple\"\n", - "assert str(len(e)) == \"3\", \"length of e is not correct\"\n", - "assert str(sorted(map(str, e))) == \"['-3.3', '5', 'd']\", \"values of e are not correct\"\n", - "assert str(e) == \"(5, -3.3, 'd')\", \"order of elements of e is not correct\"\n", - "\n", - "assert str(type(f)) == \"\", \"type of f is not bool. f should be a bool\"\n", - "assert str(f) == \"False\", \"boolean value of f is not correct\"\n", - "\n", - "print('Success!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "cc9122036c0a86a93446786109a3558e", - "grade": true, - "grade_id": "cell-13479eb0e5fff152", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "\n", - "# a function that checks whether a function throws an error\n", - "\"\"\"Check that squares raises an error for invalid inputs\"\"\"\n", - "def test_func_throws(func, ErrorType):\n", - " try:\n", - " func()\n", - " except ErrorType:\n", - " return True\n", - " else:\n", - " print('Did not raise right type of error!')\n", - " return False\n", - "\n", - "# test a custom function\n", - "from hashlib import sha1\n", - "assert str(type(fun(3))) == \"\", \"type of fun(3) is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(fun(3)) == \"3\", \"value of fun(3) is not correct\"\n", - "\n", - "assert str(type(test_func_throws(lambda : fun(-4), ValueError))) == \"\", \"type of test_func_throws(lambda : fun(-4), ValueError) is not bool. test_func_throws(lambda : fun(-4), ValueError) should be a bool\"\n", - "assert str(test_func_throws(lambda : fun(-4), ValueError)) == \"True\", \"boolean value of test_func_throws(lambda : fun(-4), ValueError) is not correct\"\n", - "\n", - "assert str(type(test_func_throws(lambda : fun(0), ValueError))) == \"\", \"type of test_func_throws(lambda : fun(0), ValueError) is not bool. test_func_throws(lambda : fun(0), ValueError) should be a bool\"\n", - "assert str(test_func_throws(lambda : fun(0), ValueError)) == \"False\", \"boolean value of test_func_throws(lambda : fun(0), ValueError) is not correct\"\n", - "\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-multi-unchanged.ipynb b/nbgrader/tests/apps/files/autotest-multi-unchanged.ipynb deleted file mode 100644 index 6090389a7..000000000 --- a/nbgrader/tests/apps/files/autotest-multi-unchanged.ipynb +++ /dev/null @@ -1,257 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "raise NotImplementedError()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "620860a42fa05b556da01013ef99ed8c", - "grade": true, - "grade_id": "test_multi", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "# the basic tests from the simple notebook\n", - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of b is not str. b should be an str\"\n", - "assert str(len(b)) == \"5\", \"length of b is not correct\"\n", - "assert str(b.lower()) == \"hello\", \"value of b is not correct\"\n", - "assert str(b) == \"hello\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of c is not list. c should be a list\"\n", - "assert str(len(c)) == \"3\", \"length of c is not correct\"\n", - "assert str(sorted(map(str, c))) == \"['1', '2', 'test']\", \"values of c are not correct\"\n", - "assert str(c) == \"[1, 2, 'test']\", \"order of elements of c is not correct\"\n", - "\n", - "print('Success!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "c46928757ed303ce204ef97299b39f82", - "grade": true, - "grade_id": "cell-f2803ba7c42d03ab", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "# multiple expressions per line\n", - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of type(a) is not correct\"\n", - "\n", - "assert str(type(str(a))) == \"\", \"type of str(a) is not str. str(a) should be an str\"\n", - "assert str(len(str(a))) == \"1\", \"length of str(a) is not correct\"\n", - "assert str(str(a).lower()) == \"5\", \"value of str(a) is not correct\"\n", - "assert str(str(a)) == \"5\", \"correct string value of str(a) but incorrect case of letters\"\n", - "\n", - "assert str(type(a == \"5\")) == \"\", \"type of a == \\\"5\\\" is not bool. a == \\\"5\\\" should be a bool\"\n", - "assert str(a == \"5\") == \"False\", \"boolean value of a == \\\"5\\\" is not correct\"\n", - "\n", - "assert str(type([ch for ch in b])) == \"\", \"type of [ch for ch in b] is not list. [ch for ch in b] should be a list\"\n", - "assert str(len([ch for ch in b])) == \"5\", \"length of [ch for ch in b] is not correct\"\n", - "assert str(sorted(map(str, [ch for ch in b]))) == \"['e', 'h', 'l', 'l', 'o']\", \"values of [ch for ch in b] are not correct\"\n", - "assert str([ch for ch in b]) == \"['h', 'e', 'l', 'l', 'o']\", \"order of elements of [ch for ch in b] is not correct\"\n", - "\n", - "assert str(type(len(c))) == \"\", \"type of len(c) is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(len(c)) == \"3\", \"value of len(c) is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "\n", - "# intervening regular code\n", - "print(\"hello!\")\n", - "\n", - "# differing spacings, numbers of comment characters, trailing whitespace\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "\n", - "\n", - "print('Success!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "2fc8066fae8e7aeb865116c88d1be31a", - "grade": true, - "grade_id": "cell-693350420ec62f1b", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "# a few common types\n", - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of b is not str. b should be an str\"\n", - "assert str(len(b)) == \"5\", \"length of b is not correct\"\n", - "assert str(b.lower()) == \"hello\", \"value of b is not correct\"\n", - "assert str(b) == \"hello\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of c is not list. c should be a list\"\n", - "assert str(len(c)) == \"3\", \"length of c is not correct\"\n", - "assert str(sorted(map(str, c))) == \"['1', '2', 'test']\", \"values of c are not correct\"\n", - "assert str(c) == \"[1, 2, 'test']\", \"order of elements of c is not correct\"\n", - "\n", - "assert str(type(d)) == \"\", \"type of d is not dict. d should be a dict\"\n", - "assert str(len(list(d.keys()))) == \"2\", \"number of keys of d is not correct\"\n", - "assert str(sorted(map(str, d.keys()))) == \"['3', 'a']\", \"keys of d are not correct\"\n", - "assert str(sorted(map(str, d.values()))) == \"['[1.2, 3]', 'f']\", \"correct keys, but values of d are not correct\"\n", - "assert str(d) == \"{'a': 'f', 3: [1.2, 3]}\", \"correct keys and values, but incorrect correspondence in keys and values of d\"\n", - "\n", - "assert str(type(e)) == \"\", \"type of e is not tuple. e should be a tuple\"\n", - "assert str(len(e)) == \"3\", \"length of e is not correct\"\n", - "assert str(sorted(map(str, e))) == \"['-3.3', '5', 'd']\", \"values of e are not correct\"\n", - "assert str(e) == \"(5, -3.3, 'd')\", \"order of elements of e is not correct\"\n", - "\n", - "assert str(type(f)) == \"\", \"type of f is not bool. f should be a bool\"\n", - "assert str(f) == \"False\", \"boolean value of f is not correct\"\n", - "\n", - "print('Success!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "cc9122036c0a86a93446786109a3558e", - "grade": true, - "grade_id": "cell-13479eb0e5fff152", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "\n", - "# a function that checks whether a function throws an error\n", - "\"\"\"Check that squares raises an error for invalid inputs\"\"\"\n", - "def test_func_throws(func, ErrorType):\n", - " try:\n", - " func()\n", - " except ErrorType:\n", - " return True\n", - " else:\n", - " print('Did not raise right type of error!')\n", - " return False\n", - "\n", - "# test a custom function\n", - "from hashlib import sha1\n", - "assert str(type(fun(3))) == \"\", \"type of fun(3) is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(fun(3)) == \"3\", \"value of fun(3) is not correct\"\n", - "\n", - "assert str(type(test_func_throws(lambda : fun(-4), ValueError))) == \"\", \"type of test_func_throws(lambda : fun(-4), ValueError) is not bool. test_func_throws(lambda : fun(-4), ValueError) should be a bool\"\n", - "assert str(test_func_throws(lambda : fun(-4), ValueError)) == \"True\", \"boolean value of test_func_throws(lambda : fun(-4), ValueError) is not correct\"\n", - "\n", - "assert str(type(test_func_throws(lambda : fun(0), ValueError))) == \"\", \"type of test_func_throws(lambda : fun(0), ValueError) is not bool. test_func_throws(lambda : fun(0), ValueError) should be a bool\"\n", - "assert str(test_func_throws(lambda : fun(0), ValueError)) == \"False\", \"boolean value of test_func_throws(lambda : fun(0), ValueError) is not correct\"\n", - "\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/nbgrader/tests/apps/files/autotest-multi.ipynb b/nbgrader/tests/apps/files/autotest-multi.ipynb deleted file mode 100644 index 7488e64df..000000000 --- a/nbgrader/tests/apps/files/autotest-multi.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "nbgrader": { - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "### BEGIN SOLUTION\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]\n", - "d = {\"a\" : \"f\", 3 : [1.2, 3]}\n", - "e = (5, -3.3, \"d\")\n", - "f = False\n", - "def fun(x):\n", - " if x < 0:\n", - " raise ValueError\n", - " else:\n", - " return x\n", - "### END SOLUTION" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "test_multi", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "# the basic tests from the simple notebook\n", - "### AUTOTEST a\n", - "### AUTOTEST b\n", - "### AUTOTEST c" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "cell-f2803ba7c42d03ab", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "# multiple expressions per line\n", - "### AUTOTEST type(a); str(a); a == \"5\";\n", - "### AUTOTEST [ch for ch in b]; len(c);\n", - "### AUTOTEST a;\n", - "\n", - "# intervening regular code\n", - "print(\"hello!\")\n", - "\n", - "# differing spacings, numbers of comment characters, trailing whitespace\n", - "### AUTOTEST a \n", - "#AUTOTEST a\n", - "# AUTOTEST a\n", - "## AUTOTEST a\n", - "# AUTOTEST a\n", - "# # # # AUTOTEST a\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "cell-693350420ec62f1b", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "# a few common types\n", - "### AUTOTEST a; b; c; d; e; f;" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "cell-13479eb0e5fff152", - "locked": true, - "points": 1, - "schema_version": 3, - "solution": false, - "task": false - } - }, - "outputs": [], - "source": [ - "\n", - "# a function that checks whether a function throws an error\n", - "\"\"\"Check that squares raises an error for invalid inputs\"\"\"\n", - "def test_func_throws(func, ErrorType):\n", - " try:\n", - " func()\n", - " except ErrorType:\n", - " return True\n", - " else:\n", - " print('Did not raise right type of error!')\n", - " return False\n", - "\n", - "# test a custom function\n", - "### AUTOTEST fun(3)\n", - "### AUTOTEST test_func_throws(lambda : fun(-4), ValueError)\n", - "### AUTOTEST test_func_throws(lambda : fun(0), ValueError)\n" - ] - } - ], - "metadata": { - "celltoolbar": "Create Assignment", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-simple-changed.ipynb b/nbgrader/tests/apps/files/autotest-simple-changed.ipynb deleted file mode 100644 index d918a0df8..000000000 --- a/nbgrader/tests/apps/files/autotest-simple-changed.ipynb +++ /dev/null @@ -1,92 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "f1736680e92dee1b5debd87ec5790f10", - "grade": true, - "grade_id": "test_simple", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of b is not str. b should be an str\"\n", - "assert str(len(b)) == \"5\", \"length of b is not correct\"\n", - "assert str(b.lower()) == \"hello\", \"value of b is not correct\"\n", - "assert str(b) == \"hello\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of c is not list. c should be a list\"\n", - "assert str(len(c)) == \"3\", \"length of c is not correct\"\n", - "assert str(sorted(map(str, c))) == \"['1', '2', 'test']\", \"values of c are not correct\"\n", - "assert str(c) == \"[1, 2, 'test']\", \"order of elements of c is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-simple-unchanged.ipynb b/nbgrader/tests/apps/files/autotest-simple-unchanged.ipynb deleted file mode 100644 index 214e5fbbb..000000000 --- a/nbgrader/tests/apps/files/autotest-simple-unchanged.ipynb +++ /dev/null @@ -1,90 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "nbgrader": { - "cell_type": "code", - "checksum": "e0106b7b1e000112c4ce2c8f8fa64ed1", - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "# YOUR CODE HERE\n", - "raise NotImplementedError()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "deletable": false, - "editable": false, - "max_height": 100, - "nbgrader": { - "cell_type": "code", - "checksum": "f1736680e92dee1b5debd87ec5790f10", - "grade": true, - "grade_id": "test_simple", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "from hashlib import sha1\n", - "assert str(type(a)) == \"\", \"type of a is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()\"\n", - "assert str(a) == \"5\", \"value of a is not correct\"\n", - "\n", - "assert str(type(b)) == \"\", \"type of b is not str. b should be an str\"\n", - "assert str(len(b)) == \"5\", \"length of b is not correct\"\n", - "assert str(b.lower()) == \"hello\", \"value of b is not correct\"\n", - "assert str(b) == \"hello\", \"correct string value of b but incorrect case of letters\"\n", - "\n", - "assert str(type(c)) == \"\", \"type of c is not list. c should be a list\"\n", - "assert str(len(c)) == \"3\", \"length of c is not correct\"\n", - "assert str(sorted(map(str, c))) == \"['1', '2', 'test']\", \"values of c are not correct\"\n", - "assert str(c) == \"[1, 2, 'test']\", \"order of elements of c is not correct\"\n", - "\n", - "print('Success!')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotest-simple.ipynb b/nbgrader/tests/apps/files/autotest-simple.ipynb deleted file mode 100644 index 42c87b071..000000000 --- a/nbgrader/tests/apps/files/autotest-simple.ipynb +++ /dev/null @@ -1,74 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a variable named `a` with value `5`, a variable `b` with value `\"hello\"`, and a variable `c` with value `[1, 2, \"test\"]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": false, - "grade_id": "soln", - "locked": false, - "schema_version": 3, - "solution": true - } - }, - "outputs": [], - "source": [ - "### BEGIN SOLUTION\n", - "a = 5\n", - "b = \"hello\"\n", - "c = [1, 2, \"test\"]\n", - "### END SOLUTION" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "test_simple", - "locked": false, - "points": 1, - "schema_version": 3, - "solution": false - } - }, - "outputs": [], - "source": [ - "### AUTOTEST a\n", - "### AUTOTEST b\n", - "### AUTOTEST c" - ] - } - ], - "metadata": { - "celltoolbar": "Create Assignment", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/autotests.yml b/nbgrader/tests/apps/files/autotests.yml deleted file mode 100644 index cfd000cb5..000000000 --- a/nbgrader/tests/apps/files/autotests.yml +++ /dev/null @@ -1,310 +0,0 @@ -#kernel_name: -# setup: -# hash: -# dispatch: -# normalize: -# check: -# success: -# -# templates: -# default: -# - test: -# fail: -# -# datatype: -# - test: -# fail: - - -python3: - setup: "from hashlib import sha1" - hash: 'sha1({{snippet}}.encode("utf-8")+b"{{salt}}").hexdigest()' - dispatch: "type({{snippet}})" - normalize: "str({{snippet}})" - check: 'assert {{snippet}} == "{{value}}", "{{message}}"' - success: "print('Success!')" - - templates: - default: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - int: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - float: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not float. Please make sure it is float and not np.float64, etc. You can cast your value into a float using float()" - - - test: "round({{snippet}}, 2)" - fail: "value of {{snippet}} is not correct (rounded to 2 decimal places)" - - set: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not set. {{snippet}} should be a set" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - list: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not list. {{snippet}} should be a list" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sorted(map(str, {{snippet}}))" - fail: "values of {{snippet}} are not correct" - - - test: "{{snippet}}" - fail: "order of elements of {{snippet}} is not correct" - - tuple: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not tuple. {{snippet}} should be a tuple" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sorted(map(str, {{snippet}}))" - fail: "values of {{snippet}} are not correct" - - - test: "{{snippet}}" - fail: "order of elements of {{snippet}} is not correct" - - str: - - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not str. {{snippet}} should be an str" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "{{snippet}}.lower()" - fail: "value of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "correct string value of {{snippet}} but incorrect case of letters" - - dict: - - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not dict. {{snippet}} should be a dict" - - - test: "len(list({{snippet}}.keys()))" - fail: "number of keys of {{snippet}} is not correct" - - - test: "sorted(map(str, {{snippet}}.keys()))" - fail: "keys of {{snippet}} are not correct" - - - test: "sorted(map(str, {{snippet}}.values()))" - fail: "correct keys, but values of {{snippet}} are not correct" - - - test: "{{snippet}}" - fail: "correct keys and values, but incorrect correspondence in keys and values of {{snippet}}" - - bool: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not bool. {{snippet}} should be a bool" - - - test: "{{snippet}}" - fail: "boolean value of {{snippet}} is not correct" - - type: - - test: "{{snippet}}" - fail: "type of {{snippet}} is not correct" - -# --------------------------------------------- - -python: - setup: "from hashlib import sha1" - hash: 'sha1({{snippet}}.encode("utf-8")+b"{{salt}}").hexdigest()' - dispatch: "type({{snippet}})" - normalize: "str({{snippet}})" - check: 'assert {{snippet}} == "{{value}}", "{{message}}"' - success: "print('Success!')" - - templates: - default: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - int: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not int. Please make sure it is int and not np.int64, etc. You can cast your value into an int using int()" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - float: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not float. Please make sure it is float and not np.float64, etc. You can cast your value into a float using float()" - - - test: "round({{snippet}}, 2)" - fail: "value of {{snippet}} is not correct (rounded to 2 decimal places)" - - set: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not set. {{snippet}} should be a set" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - list: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not list. {{snippet}} should be a list" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sorted(map(str, {{snippet}}))" - fail: "values of {{snippet}} are not correct" - - - test: "{{snippet}}" - fail: "order of elements of {{snippet}} is not correct" - - tuple: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not tuple. {{snippet}} should be a tuple" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sorted(map(str, {{snippet}}))" - fail: "values of {{snippet}} are not correct" - - - test: "{{snippet}}" - fail: "order of elements of {{snippet}} is not correct" - - str: - - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not str. {{snippet}} should be an str" - - - test: "len({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "{{snippet}}.lower()" - fail: "value of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "correct string value of {{snippet}} but incorrect case of letters" - - dict: - - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not dict. {{snippet}} should be a dict" - - - test: "len(list({{snippet}}.keys()))" - fail: "number of keys of {{snippet}} is not correct" - - - test: "sorted(map(str, {{snippet}}.keys()))" - fail: "keys of {{snippet}} are not correct" - - - test: "sorted(map(str, {{snippet}}.values()))" - fail: "correct keys, but values of {{snippet}} are not correct" - - - test: "{{snippet}}" - fail: "correct keys and values, but incorrect correspondence in keys and values of {{snippet}}" - - bool: - - test: "type({{snippet}})" - fail: "type of {{snippet}} is not bool. {{snippet}} should be a bool" - - - test: "{{snippet}}" - fail: "boolean value of {{snippet}} is not correct" - - type: - - test: "{{snippet}}" - fail: "type of {{snippet}} is not correct" - - - -# -------------------------------------------------------------------------------------------------- -ir: - setup: 'library(digest)' - hash: 'digest(paste({{snippet}}, "{{salt}}"))' - dispatch: 'class({{snippet}})' - normalize: 'toString({{snippet}})' - check: 'stopifnot("{{message}}"= setequal({{snippet}}, "{{value}}"))' - success: "print('Success!')" - - templates: - default: - - test: "class({{snippet}})" - fail: "type of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "value of {{snippet}} is not correct" - - integer: - - test: "class({{snippet}})" - fail: "type of {{snippet}} is not integer" - - - test: "length({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sort({{snippet}})" - fail: "values of {{snippet}} are not correct" - - numeric: - - test: "class({{snippet}})" - fail: "type of {{snippet}} is not double" - - - test: "round({{snippet}}, 2)" - fail: "value of {{snippet}} is not correct (rounded to 2 decimal places)" - - - test: "length({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sort({{snippet}})" - fail: "values of {{snippet}} are not correct" - - list: - - test: "class({{snippet}})" - fail: "type of {{snippet}} is not list" - - - test: "length({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "sort(c(names({{snippet}})))" - fail: "values of {{snippet}} names are not correct" - - - test: "{{snippet}}" - fail: "order of elements of {{snippet}} is not correct" - - character: - - test: "class({{snippet}})" - fail: "type of {{snippet}} is not list" - - - test: "length({{snippet}})" - fail: "length of {{snippet}} is not correct" - - - test: "tolower({{snippet}})" - fail: "value of {{snippet}} is not correct" - - - test: "{{snippet}}" - fail: "correct string value of {{snippet}} but incorrect case of letters" - - logical: - - test: "class({{snippet}})" - fail: "type of {{snippet}} is not logical" - - - test: "{{snippet}}" - fail: "logical value of {{snippet}} is not correct" diff --git a/nbgrader/tests/apps/files/data.txt b/nbgrader/tests/apps/files/data.txt deleted file mode 100644 index 0f8a25a9d..000000000 --- a/nbgrader/tests/apps/files/data.txt +++ /dev/null @@ -1,4 +0,0 @@ -line 1 -line 2 -line 3 -line 4 \ No newline at end of file diff --git a/nbgrader/tests/apps/files/gradebook.db b/nbgrader/tests/apps/files/gradebook.db deleted file mode 100644 index a9a3ebbb4f7fadc1fc38790e68837f57c9d5367d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35840 zcmeI5O>Y}T7{~X;v6F4x_jc2!flYm|S8`NrD^e>!;F@h}HFc=#0u_hV*4_jw_HK#S z^hGKt3GH{_#+4g44txQ6;efsw=%C&MmS1QUoPeYH$o)j)Zi!( z2oXs0ub32biQ&)j&p4L*jr*S2VJ=P9<{X&*WcP70C9bTnPZnL7#SjV1Fx^Uu7GMmi zHK4{ARHtC2he~T$YoJOE-FL)1a*5^eoiz^AbLy zbCj#=b)~vmvz0J8YEN0JD_51eQmZJpC@GF7mmw<>ZIZP-v!k#PWYGT*e5YIggT!P7 zFZetf2xjfUAkA-}{Le7F!3q2dpI|cVuk5${2LCJ=Ds~?ob`)Yy-g7c^b6acaYE$oY zaOo(Ojo3aR2SZXQNd?8B?p!+Tg@HXtuEOiRCOnpVe=kg#v=;Vaja%5^D+`%|q+xop#H>Dj!L?P}Xw zE%UySl|xxg9)rwI-|nSn>BuBY&!xo0MRu?3gxT+h|9+SX zv2XoX0VWbJJOqy$JMHr(PmF#j$RWN|DOMIoN#}f5)uLZIa#4s>q2O0Y z^FO%Rc3|>Pl$=#sN4jz>DONM1oamYT?WUWxXbiv~PQw5Hf^iNCKp=JmVE!Mwkb}cO zARqwwAE1B&5QrTCxc`q`$iZPC5D2*i$negB_eE->6X+zk6OD*S?e8RySqu_vEMiG>3DgO)1xGU0(++G3PF; zhZfW`4fURGXdUaJt0pqaXV72OE^Z!)qHzU$&(!V-r9o0xAIa! zMyunk9FHmjD-t5GQ~T78?Uj$F#7u^LT6ND1EGr`#KS7@hc%KO;!FX*b)?50k4-V)* zJ7#kWp9++A)a{(qP(EqcXF&5ay}8xvcY_nnS9<&4hn#7X#fe1aK?x}VXCE3;FZ!N; zIQX_JuYEcRGR*&DH{^rEKp-Fh_x}M3C;)-j5rF)UUC6;)4uSt zV)hF9s^7Rjuj^_?U+aX-lfpQ5`G23$ zflRJRzC^#@#DhC zqX$p3`<*7g1j4t?S5CfWBg7X#_>G7#|BpD#!EQic3JJjd{}culTmk|S2|)fw1O>YR zfhi;a_y1EERB#ChL?i&$|A?SqHy|*D1R(#XFsR@X5Qs>+R_d6dBSY`&-{}IC& Kn1MhH2>b&?oziOn diff --git a/nbgrader/tests/apps/files/infinite-loop-with-output.ipynb b/nbgrader/tests/apps/files/infinite-loop-with-output.ipynb deleted file mode 100644 index 44b2fe82b..000000000 --- a/nbgrader/tests/apps/files/infinite-loop-with-output.ipynb +++ /dev/null @@ -1,26 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "x = []\n", - "while True:\n", - " x.append(1)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python", - "language": "python", - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/infinite-loop.ipynb b/nbgrader/tests/apps/files/infinite-loop.ipynb deleted file mode 100644 index b32eaf15d..000000000 --- a/nbgrader/tests/apps/files/infinite-loop.ipynb +++ /dev/null @@ -1,25 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "while True:\n", - " pass" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python", - "language": "python", - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/nbgrader/tests/apps/files/jupyter.png b/nbgrader/tests/apps/files/jupyter.png deleted file mode 100644 index 9848cfb961d106ecb1fdc63173b628f6a793493c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21090 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 ACjbBd diff --git a/nbgrader/tests/apps/files/myexporter.py b/nbgrader/tests/apps/files/myexporter.py deleted file mode 100644 index 470606d05..000000000 --- a/nbgrader/tests/apps/files/myexporter.py +++ /dev/null @@ -1,6 +0,0 @@ -from nbgrader.plugins.export import ExportPlugin - -class MyExporter(ExportPlugin): - def export(self, gradebook): - with open(self.to, "w") as fh: - fh.write("hello!") diff --git a/nbgrader/tests/apps/files/notebooks.zip b/nbgrader/tests/apps/files/notebooks.zip deleted file mode 100644 index a77940f2685ca3b10b8dfcc583cd58e050b1d7b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18018 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