• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

uw-it-aca / gradepage / 18618285531

18 Oct 2025 04:44PM UTC coverage: 61.156% (+0.06%) from 61.096%
18618285531

push

github

web-flow
Merge pull request #743 from uw-it-aca/task/graderoster-for-section

refactor graderoster_for_section

35 of 50 new or added lines in 5 files covered. (70.0%)

3 existing lines in 1 file now uncovered.

2042 of 3339 relevant lines covered (61.16%)

0.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.43
/course_grader/views/api/__init__.py
1
# Copyright 2025 UW-IT, University of Washington
2
# SPDX-License-Identifier: Apache-2.0
3

4

5
from django.conf import settings
1✔
6
from userservice.user import UserService
1✔
7
from uw_sws_graderoster.models import GradingScale
1✔
8
from course_grader.dao.person import person_display_name
1✔
9
from course_grader.dao.term import (
1✔
10
    submission_deadline_warning, is_grading_period_open)
11
from course_grader.views.rest_dispatch import RESTDispatch
1✔
12
from course_grader.models import Grade
1✔
13
from course_grader.exceptions import OverrideNotPermitted, NoGradableStudents
1✔
14
from logging import getLogger
1✔
15

16
logger = getLogger(__name__)
1✔
17

18

19
class GradeFormHandler(RESTDispatch):
1✔
20
    def valid_user_override(self):
1✔
21
        if (not getattr(settings, "ALLOW_GRADE_SUBMISSION_OVERRIDE", False) and
×
22
                UserService().get_override_user() is not None):
23
            raise OverrideNotPermitted()
×
24

25
    def save_grade(self, section_id, data):
1✔
26
        try:
×
27
            student_id = data["student_id"]
×
28
            (student_reg_id, duplicate_code) = student_id.split("-", 1)
×
29
        except ValueError:
×
30
            student_reg_id = student_id
×
31
            duplicate_code = ""
×
32

33
        try:
×
34
            grade = Grade.objects.get(section_id=section_id,
×
35
                                      student_reg_id=student_reg_id,
36
                                      duplicate_code=duplicate_code,
37
                                      modified_by=self.user.uwregid)
38
        except Grade.DoesNotExist:
×
39
            grade = Grade(section_id=section_id,
×
40
                          student_reg_id=student_reg_id,
41
                          duplicate_code=duplicate_code,
42
                          modified_by=self.user.uwregid)
43

44
        action = "saved"
×
45
        if "grade" in data:
×
46
            grade.grade = data["grade"]
×
47
        if "is_incomplete" in data:
×
48
            grade.is_incomplete = data["is_incomplete"]
×
49
        if "is_writing" in data:
×
50
            grade.is_writing = data["is_writing"]
×
51
        if "no_grade_now" in data:
×
52
            grade.no_grade_now = data["no_grade_now"]
×
53
        if "import_source" in data:
×
54
            grade.import_source = data["import_source"]
×
55
        if "import_grade" in data:
×
56
            grade.import_grade = data["import_grade"]
×
57
            action = "imported"
×
58
        if "is_override_grade" in data:
×
59
            grade.is_override_grade = data["is_override_grade"]
×
60
        if "comment" in data:
×
61
            grade.comment = data["comment"]
×
62
        grade.save()
×
63

64
        logged_grade = "{}".format("X" if grade.no_grade_now else grade.grade)
×
65
        if not len(logged_grade):
×
66
            logged_grade = "None"
×
67
        if grade.is_incomplete:
×
68
            logged_grade = "I," + logged_grade
×
69
        if grade.is_writing:
×
70
            logged_grade += ",W"
×
71

72
        logger.info("Grade {}, Student: {}, Section: {}, Grade: {}".format(
×
73
            action, student_id, section_id, logged_grade))
74

75
        return grade
×
76

77

78
def sorted_students(students):
1✔
79
    return sorted(students, key=lambda s: (
1✔
80
        s.student_surname.upper(), s.student_first_name.upper(),
81
        s.section_id))
82

83

84
def sorted_grades(grades):
1✔
85
    return GradingScale().sorted_scale(grades)
1✔
86

87

88
def item_is_submitted(item):
1✔
89
    if (item.is_auditor or item.date_withdrawn is not None):
1✔
90
        return False
1✔
91

92
    # Old receipts do not include date_graded, so also check for the
93
    # existence of a grade
94
    return (item.date_graded is not None or
1✔
95
            item.grade is not None or
96
            item.no_grade_now)
97

98

99
def graderoster_status_params(graderoster,
1✔
100
                              secondary_section_id=None,
101
                              include_grade_imports=False):
102
    total_count = 0
1✔
103
    submitted_count = 0
1✔
104
    for item in graderoster.items:
1✔
105
        if (secondary_section_id is not None and
1✔
106
                secondary_section_id != item.section_id):
107
            continue
×
108

109
        if item.is_auditor or item.date_withdrawn:
1✔
110
            continue
1✔
111

112
        total_count += 1
1✔
113
        if item_is_submitted(item):
1✔
114
            submitted_count += 1
1✔
115

116
    if total_count == 0:
1✔
117
        raise NoGradableStudents()
×
118

119
    data = {
1✔
120
        "submitted_count": submitted_count,
121
        "unsubmitted_count": total_count - submitted_count,
122
        "submitted_date": None,
123
        "submitted_by": None,
124
        "accepted_date": None,
125
        "grade_import": None,
126
        "status_code": None,
127
        "deadline_warning": False,
128
    }
129

130
    section = graderoster.section
1✔
131
    if hasattr(graderoster, "submissions"):
1✔
NEW
132
        submission = None
×
NEW
133
        if secondary_section_id is not None:
×
NEW
134
            submission = next((s for s in graderoster.submissions if (
×
135
                s["submission_id"] == secondary_section_id)), None)
136

137
        if submission is None:
×
NEW
138
            submission = next((s for s in graderoster.submissions if (
×
139
                s["submission_id"] == section.section_id)), None)
140

141
        if submission is not None:
×
142
            submitted_by = submission["submitted_by"]
×
143
            data["status_code"] = submission["status_code"]
×
NEW
144
            data["submitted_date"] = submission["submitted_date"]
×
NEW
145
            data["accepted_date"] = submission["accepted_date"]
×
146
            data["submitted_by"] = person_display_name(submitted_by)
×
147
            if include_grade_imports:
×
148
                grade_import = submission["grade_import"]
×
149
                data["grade_import"] = grade_import.json_data() if (
×
150
                    grade_import is not None) else None
151

152
    if (is_grading_period_open(section) and data["unsubmitted_count"]):
1✔
153
        data["deadline_warning"] = submission_deadline_warning(section.term)
1✔
154

155
    return data
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc