-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
143 lines (116 loc) · 4.26 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import csv
import os
import sys
from datetime import datetime
from dotenv import load_dotenv
from progress.bar import Bar
from filters import fetch_url, get_badge_filter, get_dates, get_year, get_year_filter
from model import Achievements, ProcessedStudent, Student, Users
load_dotenv()
AUTH = os.getenv("AUTH")
HOST = os.getenv("HOST")
if not AUTH or not HOST or not os.getenv("TRACKED_BADGES"):
raise ValueError("Missing environment variables")
USERS_URL = HOST + "/api/user"
ACHIEVEMENTS_URL = HOST + "/learning/evidenceFeed/user"
headers = {
"Authorization": AUTH,
"Accept": "application/json",
"Content-Type": "application/json",
}
TRACKED_BADGES = os.getenv("TRACKED_BADGES").split(",")
def main():
year = get_year()
start_date, end_date = get_dates()
students = get_students(year)
processed = get_processed(start_date, end_date, students)
# for student in processed:
# print(student.fullName, student.externalId)
# for badge in student.badges:
# print(f" {badge.name}: {badge.count}")
write_out(year, processed)
def get_students(year):
user_filter = get_year_filter(year)
cursor = None
progress_bar = None
students: list[Student] = []
while True:
res = fetch_url(f"{USERS_URL}?filter={user_filter}&cursor={cursor}", headers)
if res.status_code != 200:
print(f"Failed to get users: {res.text}")
sys.exit(1)
users = Users.model_validate_json(res.text)
cursor = users.metadata.cursor
students += users.data
if not progress_bar:
progress_bar = Bar("Fetching students", max=users.metadata.count)
progress_bar.next(len(users.data))
if len(students) >= users.metadata.count:
break
progress_bar.finish()
return students
def get_processed(start_date, end_date, students):
processed: list[ProcessedStudent] = []
progress_bar = Bar("Fetching badges", max=len(students))
for student in students:
p = ProcessedStudent(
schoolboxId=student.id,
externalId=(
int(student.externalId) if student.externalId is not None else -1
),
fullName=student.fullName,
firstName=student.firstName,
lastName=student.lastName,
badges={},
)
achievements_filter = get_badge_filter()
cursor = None
seen = 0
while True:
url = f"{ACHIEVEMENTS_URL}/{student.id}?filter={achievements_filter}"
if cursor:
url += f"&cursor={cursor}"
res = fetch_url(url, headers=headers)
if res.status_code != 200:
print(f"Failed to get achievements: {res.text}")
sys.exit(1)
achievements = Achievements.model_validate_json(res.text)
for achievement in achievements.data:
seen += 1
# ensure the badge is between the start and end date
# achievement.date is RFC3339 format, i.e. "2024-07-30T11:07:35+10:00"
badge_date = datetime.strptime(achievement.date, "%Y-%m-%dT%H:%M:%S%z")
if badge_date < start_date or badge_date >= end_date:
continue
badge = achievement.object.badge.name
p.add_badge(badge)
if (
seen == achievements.metadata.count
or not achievements.metadata.cursor.next
):
break
cursor = achievements.metadata.cursor.next
progress_bar.next()
processed.append(p)
progress_bar.finish()
return processed
def write_out(year, processed):
with open(f"{year}-output.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
header = [
"Student Number",
"First Name",
"Last Name",
] + TRACKED_BADGES
writer.writerow(header)
for student in processed:
row = [
student.externalId,
student.firstName,
student.lastName,
]
for badge in TRACKED_BADGES:
row.append(student.get_badge(badge))
writer.writerow(row)
if __name__ == "__main__":
main()