Skip to content

Commit 9322ab0

Browse files
authored
Merge pull request #152 from afeld/attendance
add attendance script
2 parents 6502cc7 + eb72084 commit 9322ab0

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

extras/scripts/attendance.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# this is a script rather than a notebook to avoid private student information being committed to the repository
2+
3+
import pandas as pd
4+
5+
NUM_CLASSES = 7
6+
FREEBIES = 1
7+
TOP_SCORE = NUM_CLASSES - FREEBIES
8+
9+
ROLL_CALL_CSV = (
10+
"~/Downloads/attendance_reports_attendance-264e4d14-1765-4396-b311-4d927b59566d.csv"
11+
)
12+
# get by clicking into the Assignment and getting from the URL
13+
ASSIGNMENT_ID = 1405957
14+
GRADEBOOK_FILE = "attendance.csv"
15+
STUDENT_UNIQUE_COLS = ["Student ID", "Student Name", "Section Name", "Section"]
16+
17+
18+
def normalize_sections(entries: pd.DataFrame):
19+
"""For students who switch sections, use their final section."""
20+
21+
student_info = entries.drop_duplicates(subset=["Student ID"], keep="last")
22+
student_info = student_info[["Student ID", "Section Name"]]
23+
24+
entries = entries.drop(columns=["Section Name"])
25+
return entries.merge(student_info, on="Student ID")
26+
27+
28+
def get_entries(filename: str):
29+
entries = pd.read_csv(
30+
filename,
31+
index_col=False,
32+
usecols=[
33+
"Section Name",
34+
"Student Name",
35+
"Student ID",
36+
"Class Date",
37+
"Attendance",
38+
],
39+
parse_dates=["Class Date"],
40+
)
41+
42+
entries = normalize_sections(entries)
43+
# pull the section number out
44+
entries["Section"] = (
45+
entries["Section Name"].str.extract(r"INAFU6504_(\d{3})_").astype(int)
46+
)
47+
48+
return entries
49+
50+
51+
def print_heading(text: str):
52+
print(f"-------------------\n\n{text.upper()}:\n")
53+
54+
55+
def print_students(students: pd.Series):
56+
print(students.droplevel(["Student ID", "Section Name"]))
57+
print()
58+
59+
60+
def validate(entries: pd.DataFrame):
61+
recording_counts = entries.groupby(STUDENT_UNIQUE_COLS).size()
62+
print_heading("Students missing entries")
63+
print_students(recording_counts[recording_counts < NUM_CLASSES])
64+
65+
total_classes = entries["Class Date"].nunique()
66+
assert total_classes == NUM_CLASSES
67+
68+
69+
def compute_scores(entries: pd.DataFrame):
70+
attended = entries[entries["Attendance"] == "present"]
71+
attendance_counts = attended.groupby(STUDENT_UNIQUE_COLS).size()
72+
# print_heading("Attendance counts")
73+
# print_students(attendance_counts)
74+
75+
# cap the top scores
76+
attendance_counts[attendance_counts > TOP_SCORE] = TOP_SCORE
77+
78+
return attendance_counts
79+
80+
81+
def write_canvas_csv(scores: pd.Series):
82+
"""https://community.canvaslms.com/t5/Instructor-Guide/How-do-I-import-grades-in-the-Gradebook/ta-p/807"""
83+
84+
attendance_col = f"Attendance ({ASSIGNMENT_ID})"
85+
86+
gradebook = (
87+
scores.reset_index(name=attendance_col)
88+
.drop(columns=["Section"])
89+
.rename(
90+
columns={
91+
# Roll Call has `FIRST LAST`, gradebook has `LAST, FIRST`. Shouldn't matter.
92+
"Student Name": "Student",
93+
"Student ID": "ID",
94+
"Section Name": "Section",
95+
}
96+
)
97+
)
98+
gradebook.to_csv(GRADEBOOK_FILE, index=False)
99+
100+
print(f"Now upload {GRADEBOOK_FILE} to CourseWorks Gradebook.")
101+
102+
103+
def run():
104+
entries = get_entries(ROLL_CALL_CSV)
105+
validate(entries)
106+
107+
scores = compute_scores(entries)
108+
print_heading("Scores")
109+
print_students(scores)
110+
111+
lowered_scores = scores[scores < TOP_SCORE]
112+
print_heading(f"Scores below {TOP_SCORE}")
113+
print_students(lowered_scores.sort_values())
114+
115+
write_canvas_csv(scores)
116+
117+
118+
run()

meta/assistant_guide.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,14 @@ It isn't your responsibility to look for potential instances of cheating/plagiar
215215
1. In the Gradebook, give points to the reviewer under the Final Project Peer Review.
216216

217217
[Scoring details.](../syllabus.md#final-project)
218+
219+
{% if id == "columbia" -%}
220+
## Final grades
221+
222+
To compute the [attendance](../syllabus.md#attendance) score:
223+
224+
1. [Export the Roll Call attendance data.](https://community.canvaslms.com/t5/Canvas-Basics-Guide/What-is-the-Roll-Call-Attendance-Tool/ta-p/59#export_attendance_data)
225+
1. Copy [the script](https://github.com/afeld/python-public-policy/blob/main/extras/scripts/attendance.py) into {{coding_env_name}}.
226+
1. Adjust the constants at the top as necessary.
227+
1. Run the code.
228+
{%- endif %}

0 commit comments

Comments
 (0)