Skip to content
This repository was archived by the owner on Jun 30, 2024. It is now read-only.

Commit 7db82a7

Browse files
committed
merge
2 parents 856460a + bad701d commit 7db82a7

File tree

4 files changed

+73
-13
lines changed

4 files changed

+73
-13
lines changed

modules/questions_report.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,12 +571,27 @@ def query_assignment(
571571
),
572572
):
573573

574+
# If a student answers no questions, then is autograded, then is removed from the course, the headings query doesn't contain this student. Add them in.
575+
username = row.question_grades.sid
576+
if username not in grades:
577+
au_row = (
578+
db(db.auth_user.username == username)
579+
.select(
580+
db.auth_user.first_name, db.auth_user.last_name, db.auth_user.email
581+
)
582+
.first()
583+
)
584+
grades[username] = dict()
585+
grades[username][None] = _UserInfo._make(
586+
[au_row.first_name, au_row.last_name, au_row.email]
587+
)
588+
574589
# Get the answer and correct info based on the type of question.
575590
question_type = grades[None][row.question_grades.div_id].type_
576591
answer, correct, timestamp = _row_decode(row, question_type)
577592

578593
# Place the query into its appropriate matrix location.
579-
grades[row.question_grades.sid][row.question_grades.div_id] = [
594+
grades[username][row.question_grades.div_id] = [
580595
timestamp,
581596
row.question_grades.score,
582597
answer,

static/js/assignment_table.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ function populateAssignmentTable() {
146146
report_type: $("#gradingoption1").val(),
147147
chap_or_assign: $("#chaporassignselector").val(),
148148
}, (data) => {
149+
// Check for errors.
150+
if ("errors" in data) {
151+
$("#assignment_info_table_loading").html(`Error<br/>Please report this error: ${escapeHTML(data.errors)}`);
152+
return;
153+
}
154+
149155
// Recreate more helpful data structures from the "flattened" data.
150156
data.orig_data.forEach(
151157
(user_id_row, user_id_index) => user_id_row.forEach((div_id_entry, div_id_index) => {

tests/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,16 @@ def hsblog(self, **kwargs):
762762
# Post to the server.
763763
return json.loads(self.test_client.validate("ajax/hsblog", data=kwargs))
764764

765+
def coursechooser(self, course_name):
766+
html = self.test_client.validate("default/coursechooser/{}".format(course_name))
767+
# Make sure this didn't send us to the user profile page to add a course we aren't registered for.
768+
assert "Course IDs for open courses" not in html
769+
770+
def removecourse(self, course_name):
771+
html = self.test_client.validate("default/removecourse/{}".format(course_name))
772+
assert "Sorry, you cannot remove" not in html
773+
assert "Course Selection" in html
774+
765775

766776
# Present ``_TestUser`` as a fixture.
767777
@pytest.fixture

tests/test_server.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ def test_grades_1(runestone_db_tools, test_user, tmp_path):
828828
test_user(
829829
"test_user_{}".format(index), "x", course, last_name="user_{}".format(index)
830830
)
831-
for index in range(3)
831+
for index in range(4)
832832
]
833833

834834
def assert_passing(index, *args, **kwargs):
@@ -850,10 +850,10 @@ def assert_passing(index, *args, **kwargs):
850850
unittest_kwargs = dict(event="unittest", div_id="units2", course=course_name)
851851

852852
# *User 0*: no data supplied
853-
# ----------------------------
853+
##----------------------------
854854

855855
# *User 1*: correct answers
856-
# ---------------------------
856+
##---------------------------
857857
# It doesn't matter which user logs out, since all three users share the same client.
858858
logout = test_user_array[2].test_client.logout
859859
logout()
@@ -867,7 +867,7 @@ def assert_passing(index, *args, **kwargs):
867867
assert_passing(1, act="percent:100:passed:2:failed:0", **unittest_kwargs)
868868

869869
# *User 2*: incorrect answers
870-
# ----------------------------
870+
##----------------------------
871871
logout()
872872
test_user_array[2].login()
873873
# Add three shortanswer answers, to make sure the number of attempts is correctly recorded.
@@ -880,8 +880,12 @@ def assert_passing(index, *args, **kwargs):
880880
)
881881
assert_passing(2, act="percent:50:passed:1:failed:1", **unittest_kwargs)
882882

883+
# *User 3*: no data supplied, and no longer in course.
884+
##----------------------------------------------------
885+
# Wait until the autograder is run to remove the student, so they will have a grade but not have any submissions.
886+
883887
# **Test the grades_report endpoint**
884-
# ====================================
888+
##====================================
885889
tu = test_user_array[2]
886890

887891
def grades_report(assignment, *args, **kwargs):
@@ -934,8 +938,22 @@ def add_to_assignment(question_kwargs, points):
934938
tu.test_client.validate("assignments/calculate_totals", **assignment_kwargs)
935939
)["success"]
936940

941+
# Remove test user 3 from the course. They can't be removed from the current course, so create a new one then add this user to it.
942+
logout()
943+
tu = test_user_array[3]
944+
tu.login()
945+
new_course = runestone_db_tools.create_course("random_course_name")
946+
tu.update_profile(course_name=new_course.course_name, is_free=True)
947+
tu.coursechooser(new_course.course_name)
948+
tu.removecourse(course_name)
949+
937950
# **Test this assignment.**
938951
# ===========================
952+
# Log back in as the instructor.
953+
logout()
954+
tu = test_user_array[2]
955+
tu.login()
956+
# Now, we can get the report.
939957
grades = json.loads(grades_report(assignment_name))
940958

941959
# Define a regex string comparison.
@@ -1017,18 +1035,20 @@ def __eq__(self, other):
10171035
["test_user_0", "user_0", "test", "test_user_0@foo.com", 0.0],
10181036
["test_user_1", "user_1", "test", "test_user_1@foo.com", 1.0],
10191037
["test_user_2", "user_2", "test", "test_user_2@foo.com", 0.2],
1038+
["test_user_3", "user_3", "test", "test_user_3@foo.com", 0.0],
10201039
],
10211040
# Correct since the first 3 questions are all on the index page.
10221041
"mergeCells": [{"col": 5, "colspan": 3, "row": 1, "rowspan": 1}],
10231042
"orig_data": [
10241043
# User 0: not submitted.
10251044
[
1026-
# The format is: ``[timestamp, score, answer, correct, num_attempts]``.
1027-
[None, 0.0, None, None, None],
1028-
[None, 0.0, None, None, None],
1029-
[None, 0.0, None, None, None],
1030-
[None, 0.0, {}, None, None],
1031-
[None, 0.0, "", None, None],
1045+
# The format is:
1046+
# ``[timestamp, score, answer, correct, num_attempts]``.
1047+
[None, 0.0, None, None, None], # shortanswer
1048+
[None, 0.0, None, None, None], # fillintheblank
1049+
[None, 0.0, None, None, None], # mchoice
1050+
[None, 0.0, {}, None, None], # lp_build
1051+
[None, 0.0, "", None, None], # activecode
10321052
],
10331053
# User 1: all correct.
10341054
[
@@ -1069,10 +1089,19 @@ def __eq__(self, other):
10691089
],
10701090
[AlmostNow(), 2.0, "percent:50:passed:1:failed:1", False, 1],
10711091
],
1092+
# User 3: not submitted.
1093+
[
1094+
# The format is:
1095+
[None, 0.0, None, None, None],
1096+
[None, 0.0, None, None, None],
1097+
[None, 0.0, None, None, None],
1098+
[None, 0.0, {}, None, None],
1099+
[None, 0.0, "", None, None],
1100+
],
10721101
],
10731102
}
10741103

1075-
# Note: on test failure, pytest will report as incorrect all the ``AlmostNow()`` and ``RegexEquals`` items, even though they make have actually compared as equal.
1104+
# Note: on test failure, pytest will report as incorrect all the ``AlmostNow()`` and ``RegexEquals`` items, even though they may have actually compared as equal.
10761105
assert grades == expected_grades
10771106

10781107
logout()

0 commit comments

Comments
 (0)