forked from AlbanyCompSci/aeries-api
-
Notifications
You must be signed in to change notification settings - Fork 2
/
gradebookdetails.py
193 lines (179 loc) · 6.71 KB
/
gradebookdetails.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python3.3
from bs4 import BeautifulSoup
import re, dateutil.parser, time
BASE_URL = 'https://abi.ausdk12.org/aeriesportal/'
DEFAULT_PAGE = 'default.aspx'
GRADEBOOK_PAGE = 'GradebookDetails.aspx'
def getGradebookDetails(gradebook_name_re, session):
page = getGradebookPage(gradebook_name_re, session)
soup = BeautifulSoup(page)
entries = getEntries(soup)
weighting = getWeighting(soup)
return {'entries': entries, 'weighting': weighting}
def getGradebookPage(gradebook_name_re, session):
default_text = session.getPage(BASE_URL + DEFAULT_PAGE)
default_soup = BeautifulSoup(default_text)
link_tag = default_soup.find('a', text=re.compile(gradebook_name_re))
js_link = link_tag.get('href')
session.executeJS(js_link)
gradebook_page = session.getPage(BASE_URL + GRADEBOOK_PAGE)
return gradebook_page
def getEntries(soup):
table_id = {'id': re.compile('ctl00_MainContent_subGBS_tblEverything')}
table = soup.find('table', table_id).find_all('table')[2]
rows = table.find_all('tr')
entries = []
for row in rows:
try:
first_td_class = row.find('td').get('class')[0]
if first_td_class != 'DataLE':
continue
except (AttributeError, TypeError):
continue
entry = getEntry(row)
entries.append(entry)
return entries
def getEntry(row):
tds = row.find_all('td', {'class': 'Data'})
entry = {
'assignment number':
clean(tds[0].get_text()),
#description/name
'description':
clean(tds[1].get_text('|').split('|')[0]),
'date assigned':
getDate(getExpand('date assigned', tds[1])),
#cannot figure out what format would look like; omitting
#'due time':
# getExpand('due time', tds[1]),
'long description':
getExpand('long description', tds[1]),
#type of assignment (Formative/Summative)
'type':
tds[2].get_text(),
#weighting category
'category':
tds[3].get_text(),
#score recieved on assignment (my points)
'recieved points':
getScore(tds[4])['numerator'],
#max score on assignment (out of points)
'max points':
getScore(tds[4])['denominator'],
#appears to be essentially the same as score; omitting
#'recieved number correct':
# getScore(tds[5])['numerator'],
#'max number correct':
# getScore(tds[5])['denominator'],
#percentage on assignment
# (could also be calculated directly from score)
'percent':
tds[6].get_text().rstrip('%'),
#unknown
'status/comment':
tds[7].get_text(),
#date grading completed
'date completed':
getDate(tds[8].get_text()),
#consider combining with due time
'due date':
getDate(tds[9].get_text()),
'grading complete':
tds[10].get_text(),
'attachments':
getAttachments(tds[11])
}
return entry
def clean(string):
cleaned = string.strip()
cleaned = cleaned.strip(' \t\n\r')
return cleaned
def getExpand(key, super_td):
sub_tr_ids = {
'date assigned': re.compile('.*trDA'),
'due time': re.compile('.*trDUT'),
'long description': re.compile('.*trCOM')
}
sub_tr = super_td.find('tr', {'id': sub_tr_ids[key]})
data_td = sub_tr.find_all('td')[1]
try:
text = clean(data_td.get_text())
except:
text = None
return text
def getScore(td):
text = clean(td.get_text())
split = text.split('/')
try:
numerator = clean(split[0])
except:
numerator = None
try:
denominator = clean(split[1])
except:
denominator = None
fraction = {'numerator': numerator, 'denominator': denominator}
return fraction
def getDate(date_str):
return dateutil.parser.parse(date_str).isoformat()
def getAttachments(td):
link_id = {'id': re.compile(("ctl(\d\d)_MainContent_subGBS_DataDetails"
"_ctl(\d\d)_dlDocuments_ctl(\d\d)_lnkDoc"))}
links = td.find_all('a', link_id)
attachments = []
for link in links:
attachment = {
'name': link.get('title'),
'file name': link.get('href')
}
attachments.append(attachment)
return attachments
def getWeighting(soup):
table = getTable(soup, 'td', ("ctl(\d\d)_MainContent_subGBS"
"_DataSummary_ctl(\d\d).*"))
rows = table.find_all('tr')
categories = []
for row in rows:
try:
row_class = row.get('class')[0]
except:
row_class = None
if row_class == 'SubHeaderRow':
continue
category = getCategory(row)
categories.append(category)
return categories
def getTable(soup, tag_name, regex_id):
tables = soup.find_all('table')
tag_id = {'id': re.compile(regex_id)}
for table in tables:
if table.find(tag_name, tag_id) != None:
last_inclusive_table = table
return last_inclusive_table
def getCategory(row):
category = {}
part_ids = {
#name of category
'name': re.compile('.*tdDESC'),
#percentage of grade when category active
'weight percent': re.compile('.*tdPctOfGrade'),
#points recieved (my points)
'recieved points': re.compile('.*tdPTS'),
#max points (out of points)
'max points': re.compile('.*tdMX'),
#my percentage (can also be calculated with
# recieved points and max points)
'grade percent': re.compile('.*tdPCT'),
#letter grade
'mark': re.compile('.*tdMK')
}
for part in part_ids:
try:
part_value = row.find('td', {'id': part_ids[part]}).get_text()
except:
category[part] = None
continue
if re.match('.*percent.*', part):
part_value = part_value.rstrip('%')
category[part] = part_value
return category