PyKubeGrader 0.3.11__py3-none-any.whl → 0.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {PyKubeGrader-0.3.11.dist-info → PyKubeGrader-0.3.13.dist-info}/METADATA +1 -1
- {PyKubeGrader-0.3.11.dist-info → PyKubeGrader-0.3.13.dist-info}/RECORD +9 -9
- pykubegrader/grade_reports/assignments.py +5 -0
- pykubegrader/grade_reports/grade_report.py +95 -17
- pykubegrader/grade_reports/grading_config.py +3 -0
- {PyKubeGrader-0.3.11.dist-info → PyKubeGrader-0.3.13.dist-info}/LICENSE.txt +0 -0
- {PyKubeGrader-0.3.11.dist-info → PyKubeGrader-0.3.13.dist-info}/WHEEL +0 -0
- {PyKubeGrader-0.3.11.dist-info → PyKubeGrader-0.3.13.dist-info}/entry_points.txt +0 -0
- {PyKubeGrader-0.3.11.dist-info → PyKubeGrader-0.3.13.dist-info}/top_level.txt +0 -0
@@ -11,10 +11,10 @@ pykubegrader/build/clean_folder.py,sha256=8N0KyL4eXRs0DCw-V_2jR9igtFs_mOFMQufdL6
|
|
11
11
|
pykubegrader/build/collate.py,sha256=cVvF7tf2U3iiH4R_dbghTcieedIx5w3Fyw9L_llInM8,6754
|
12
12
|
pykubegrader/build/markdown_questions.py,sha256=cSh8mkHK3hh-etJdgrZu9UQi1WPrKQtofkzLCUp1Z-w,4676
|
13
13
|
pykubegrader/grade_reports/__grade_reports.py,sha256=n8H_n9jdZRSPn2zlIf-GQt_Y8w91p6M8ZbdVH76Sg5k,2303
|
14
|
-
pykubegrader/grade_reports/assignments.py,sha256=
|
14
|
+
pykubegrader/grade_reports/assignments.py,sha256=I9NesLhwLelgqUekXfQCEGH-zTMAOjfb942eZYeh-Yo,7811
|
15
15
|
pykubegrader/grade_reports/class_grade_report.py,sha256=9Uuvy-xoABtjTjv-05VYhmowebgTrgEFSgh1Grws-cQ,5078
|
16
|
-
pykubegrader/grade_reports/grade_report.py,sha256=
|
17
|
-
pykubegrader/grade_reports/grading_config.py,sha256=
|
16
|
+
pykubegrader/grade_reports/grade_report.py,sha256=iTOe1sRNbsJc-Q4M_ELEMHePObLjHd0z-pm3G0sG37E,16962
|
17
|
+
pykubegrader/grade_reports/grading_config.py,sha256=FIK25yW0MbYCqCiCX9pxGLonnTjRvXFIOLrf9qZvFwY,2531
|
18
18
|
pykubegrader/grade_reports/test.ipynb,sha256=NkMZHcfBd-uJw3i1Y9ux-epBP5GiVFEhda5wxEbK0cU,808
|
19
19
|
pykubegrader/graders/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
20
20
|
pykubegrader/graders/late_assignments.py,sha256=_2-rA5RqO0BWY9WAQA_mbCxxPKTOiJOl-byD2CYWaE0,1393
|
@@ -39,9 +39,9 @@ pykubegrader/widgets_base/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-
|
|
39
39
|
pykubegrader/widgets_base/multi_select.py,sha256=WhpS7a8V3BOuEfEyFPzcDhMbgr7p1a4FFh_mKU1HLbI,4226
|
40
40
|
pykubegrader/widgets_base/reading.py,sha256=ChUS3NOTa_HLtNpxR8hGX80LPKMvYMypnR6dFknfxus,5430
|
41
41
|
pykubegrader/widgets_base/select.py,sha256=qP31bjTWIn8LEgKwtNUJbgJnum6P7bx6A_At-u1ivFk,2750
|
42
|
-
PyKubeGrader-0.3.
|
43
|
-
PyKubeGrader-0.3.
|
44
|
-
PyKubeGrader-0.3.
|
45
|
-
PyKubeGrader-0.3.
|
46
|
-
PyKubeGrader-0.3.
|
47
|
-
PyKubeGrader-0.3.
|
42
|
+
PyKubeGrader-0.3.13.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
|
43
|
+
PyKubeGrader-0.3.13.dist-info/METADATA,sha256=QXj0QbWpmsOkRnSlhMKyhpFrG8pPQcaNLqHOY_hArRk,2758
|
44
|
+
PyKubeGrader-0.3.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
45
|
+
PyKubeGrader-0.3.13.dist-info/entry_points.txt,sha256=RR57KvzDRJrP4omy5heS5cZ3E7g56YxcxJhDnp57ZU0,253
|
46
|
+
PyKubeGrader-0.3.13.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
|
47
|
+
PyKubeGrader-0.3.13.dist-info/RECORD,,
|
@@ -116,6 +116,10 @@ class Assignment(assignment_type):
|
|
116
116
|
"""
|
117
117
|
if self.exempted:
|
118
118
|
self.score = np.nan
|
119
|
+
|
120
|
+
# If the score is "---", return the score as is, this is an assignment that does not exist.
|
121
|
+
if self._score == "---":
|
122
|
+
return self.score
|
119
123
|
|
120
124
|
# Saves a table with the score of the exempted assignment still recorded.
|
121
125
|
try:
|
@@ -126,6 +130,7 @@ class Assignment(assignment_type):
|
|
126
130
|
except:
|
127
131
|
pass
|
128
132
|
return self.score
|
133
|
+
|
129
134
|
elif submission is not None:
|
130
135
|
# Adjust the score based on submission
|
131
136
|
score_ = self.grade_adjustment(submission)
|
@@ -10,6 +10,7 @@ from pykubegrader.grade_reports.grading_config import (
|
|
10
10
|
optional_drop_week,
|
11
11
|
exclude_from_running_avg,
|
12
12
|
custom_grade_adjustments,
|
13
|
+
duplicated_scores,
|
13
14
|
)
|
14
15
|
|
15
16
|
import pandas as pd
|
@@ -29,7 +30,7 @@ class GradeReport:
|
|
29
30
|
start_date (str, optional): The start date of the course. Defaults to "2025-01-06".
|
30
31
|
verbose (bool, optional): Indicates if verbose output should be displayed. Defaults to True.
|
31
32
|
"""
|
32
|
-
self.assignments, self.student_subs = get_assignments_submissions(params=params)
|
33
|
+
self.assignments, self.student_subs = get_assignments_submissions(params=params)
|
33
34
|
|
34
35
|
self.start_date = start_date
|
35
36
|
self.verbose = verbose
|
@@ -48,17 +49,47 @@ class GradeReport:
|
|
48
49
|
self.calculate_grades()
|
49
50
|
self.update_assignments_not_due_yet()
|
50
51
|
self.calculate_grades()
|
52
|
+
self.duplicate_scores()
|
51
53
|
self.drop_lowest_n_for_types(1)
|
52
54
|
self.update_weekly_table()
|
53
55
|
self._build_running_avg()
|
54
56
|
self._calculate_final_average()
|
57
|
+
df = self.highlight_nans(self.weekly_grades_df, self.weekly_grades_df_display)
|
55
58
|
if display_:
|
56
59
|
try:
|
57
|
-
display(
|
60
|
+
display(df)
|
58
61
|
display(self.weighted_average_grades)
|
59
62
|
except: # noqa: E722
|
60
63
|
pass
|
61
64
|
|
65
|
+
@staticmethod
|
66
|
+
def highlight_nans(nan_df, display_df, color='red'):
|
67
|
+
"""
|
68
|
+
Highlights NaN values from nan_df on display_df.
|
69
|
+
|
70
|
+
Parameters:
|
71
|
+
nan_df (pd.DataFrame): DataFrame containing NaNs to be highlighted.
|
72
|
+
display_df (pd.DataFrame): DataFrame to be recolored.
|
73
|
+
color (str): Background color for NaNs. Default is 'red'.
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
pd.io.formats.style.Styler: Styled DataFrame with NaNs highlighted.
|
77
|
+
"""
|
78
|
+
# Ensure both DataFrames have the same index and columns
|
79
|
+
nan_mask = nan_df.isna().reindex_like(display_df)
|
80
|
+
|
81
|
+
# Function to apply the highlight conditionally
|
82
|
+
def apply_highlight(row):
|
83
|
+
return [
|
84
|
+
f'background-color: {color}' if nan_mask.loc[row.name, col] else ''
|
85
|
+
for col in row.index
|
86
|
+
]
|
87
|
+
|
88
|
+
# Apply the highlighting row-wise
|
89
|
+
styled_df = display_df.style.apply(apply_highlight, axis=1)
|
90
|
+
|
91
|
+
return styled_df
|
92
|
+
|
62
93
|
def update_assignments_not_due_yet(self):
|
63
94
|
"""
|
64
95
|
Updates the score of assignments that are not due yet to NaN.
|
@@ -69,7 +100,24 @@ class GradeReport:
|
|
69
100
|
due_date = datetime.fromisoformat(assignment.due_date.replace("Z", "+00:00"))
|
70
101
|
if due_date > datetime.now(due_date.tzinfo) and assignment.score == 0:
|
71
102
|
assignment.score = np.nan
|
103
|
+
assignment._score = "---"
|
72
104
|
assignment.exempted = True
|
105
|
+
|
106
|
+
|
107
|
+
def color_cells(self, styler, week_list, assignment_list):
|
108
|
+
if week_list:
|
109
|
+
week = week_list.pop()
|
110
|
+
assignment = assignment_list.pop()
|
111
|
+
|
112
|
+
# Apply the style to the current cell
|
113
|
+
styler = styler.set_properties(
|
114
|
+
subset=pd.IndexSlice[[week], [assignment]],
|
115
|
+
**{'background-color': 'yellow'}
|
116
|
+
)
|
117
|
+
# Recursive call
|
118
|
+
return self.color_cells(styler, week_list, assignment_list)
|
119
|
+
else:
|
120
|
+
return styler
|
73
121
|
|
74
122
|
def _calculate_final_average(self):
|
75
123
|
total_percentage = 1
|
@@ -93,15 +141,27 @@ class GradeReport:
|
|
93
141
|
]
|
94
142
|
)
|
95
143
|
|
96
|
-
def grade_report(self):
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
144
|
+
# def grade_report(self):
|
145
|
+
# """Generates a grade report for the course.
|
146
|
+
# Returns:
|
147
|
+
# pd.DataFrame: A DataFrame containing the grade report or weekly grades only.
|
148
|
+
# """
|
149
|
+
# self._update_running_avg()
|
150
|
+
# return self.weekly_grades_df
|
151
|
+
|
104
152
|
def update_weekly_table(self):
|
153
|
+
self._update_weekly_table_nan()
|
154
|
+
self._update_weekly_table_scores()
|
155
|
+
|
156
|
+
# TODO: populate with average scores calculated from the exempted
|
157
|
+
def _update_weekly_table_scores(self):
|
158
|
+
for assignment in self.graded_assignments:
|
159
|
+
if assignment.weekly:
|
160
|
+
self.weekly_grades_df_display.loc[f"week{assignment.week}", assignment.name] = (
|
161
|
+
assignment._score
|
162
|
+
)
|
163
|
+
|
164
|
+
def _update_weekly_table_nan(self):
|
105
165
|
"""Updates the weekly grades table with the calculated scores."""
|
106
166
|
for assignment in self.graded_assignments:
|
107
167
|
if assignment.weekly:
|
@@ -114,6 +174,7 @@ class GradeReport:
|
|
114
174
|
for assignment_type, week in self.globally_exempted_assignments:
|
115
175
|
try:
|
116
176
|
self.get_graded_assignment(week, assignment_type)[0].exempted = True
|
177
|
+
self.get_graded_assignment(week, assignment_type)[0]._score = "---"
|
117
178
|
except: # noqa: E722
|
118
179
|
pass
|
119
180
|
|
@@ -293,7 +354,8 @@ class GradeReport:
|
|
293
354
|
new_weekly_grades["inds"] = inds
|
294
355
|
new_weekly_grades.set_index("inds", inplace=True)
|
295
356
|
self.weekly_grades_df = new_weekly_grades
|
296
|
-
|
357
|
+
self.weekly_grades_df_display = new_weekly_grades.copy().astype(str)
|
358
|
+
|
297
359
|
def _build_running_avg(self):
|
298
360
|
"""
|
299
361
|
Subfunction to compute and update the Running Avg row, handling NaNs.
|
@@ -302,6 +364,9 @@ class GradeReport:
|
|
302
364
|
self.weekly_grades_df.loc["Running Avg"] = self.weekly_grades_df.drop(
|
303
365
|
"Running Avg", errors="ignore"
|
304
366
|
).mean(axis=0, skipna=True)
|
367
|
+
self.weekly_grades_df_display.loc["Running Avg"] = self.weekly_grades_df.drop(
|
368
|
+
"Running Avg", errors="ignore"
|
369
|
+
).mean(axis=0, skipna=True)
|
305
370
|
|
306
371
|
def drop_lowest_n_for_types(self, n, assignments_=None):
|
307
372
|
"""
|
@@ -337,13 +402,26 @@ class GradeReport:
|
|
337
402
|
|
338
403
|
# Exempt the lowest `n` assignments
|
339
404
|
dropped = []
|
340
|
-
i =
|
405
|
+
i = 0
|
406
|
+
j = 0
|
341
407
|
while i < n:
|
342
|
-
i
|
343
|
-
valid_assignments[i].
|
344
|
-
|
408
|
+
valid_assignments[i+j].exempted = True
|
409
|
+
if valid_assignments[i+j].week in self.optional_drop_week:
|
410
|
+
j += 1
|
345
411
|
continue
|
346
|
-
dropped.append(valid_assignments[i])
|
347
|
-
self.student_assignments_dropped.append(valid_assignments[i])
|
412
|
+
dropped.append(valid_assignments[i+j])
|
413
|
+
self.student_assignments_dropped.append(valid_assignments[i+j])
|
414
|
+
i += 1
|
348
415
|
|
349
416
|
self.calculate_grades()
|
417
|
+
|
418
|
+
def duplicate_scores(self):
|
419
|
+
"""Duplicate scores from one assignment to another"""
|
420
|
+
|
421
|
+
for (week, assignment_type), (duplicate_week, duplicate_assignment_type) in duplicated_scores:
|
422
|
+
assignment = self.get_graded_assignment(week, assignment_type)[0]
|
423
|
+
duplicate_assignment = self.get_graded_assignment(duplicate_week, duplicate_assignment_type)[0]
|
424
|
+
duplicate_assignment.score = assignment.score
|
425
|
+
duplicate_assignment._score = assignment._score
|
426
|
+
duplicate_assignment.exempted = assignment.exempted
|
427
|
+
|
@@ -61,6 +61,9 @@ dropped_assignments = [
|
|
61
61
|
"labattendance",
|
62
62
|
]
|
63
63
|
|
64
|
+
# Duplicated scores, a list of tuples of assignment types and weeks where the scores will be duplicated
|
65
|
+
duplicated_scores = [[(7, "lab"), (7, "homework")]]
|
66
|
+
|
64
67
|
# TAs and other users to skip in class grade report
|
65
68
|
skipped_users = ['JCA', 'jca92', 'cnp68', 'dak329', 'xz498', 'ag4328', 'rg897', 'jce63', 'qt49']
|
66
69
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|