Last active
April 25, 2018 17:14
-
-
Save ryanlovett/a6d786eede6d5f99b7d594268b4cdaea to your computer and use it in GitHub Desktop.
Download and display an SIS course's enrollments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Example usage: | |
# $0 -H headers.json -s compsci -c c8 | |
# $0 -H headers.json -s stat -c c100 | |
import argparse | |
import json | |
import requests | |
def get_descriptors(uri, params, headers): | |
'''Get SIS descriptor IDs.''' | |
if args.debug: | |
print(uri) | |
print(params) | |
r = requests.get(uri, params=params, headers=headers) | |
data = r.json() | |
if 'fieldValues' not in data['apiResponse']['response']: | |
print(data['apiResponse']) | |
print(uri) | |
print(params) | |
raise KeyError('No fieldValues') | |
if args.debug: | |
print(data['apiResponse']['response']['fieldValues']) | |
return data['apiResponse']['response']['fieldValues'] | |
def extract_lecture_codes(sections): | |
return list( | |
map(lambda x: x['code'], | |
filter(lambda x: ' LEC ' in x['description'] or ' SES ' in x['description'] or ' WBL ' in x['description'], sections) | |
) | |
) | |
def get_enrollments(uri, params, headers): | |
if args.debug: | |
print("get_enrollments: {} {}".format(uri, params)) | |
r = requests.get(uri, params=params, headers=headers) | |
if r.status_code == 404: | |
if args.debug: print('No more enrollments') | |
return [] | |
data = r.json() | |
# Return if there is no response (e.g. 404) | |
if 'response' not in data['apiResponse']: | |
if args.debug: print('404 No response') | |
return[] | |
# Return if the UID has no enrollments | |
if 'classSectionEnrollments' not in data['apiResponse']['response']: | |
if args.debug: print('No enrollments') | |
return [] | |
enrollments = \ | |
data['apiResponse']['response']['classSectionEnrollments'] | |
params['page-number'] += 1 | |
enrollments += get_enrollments(uri, params, headers) | |
if args.debug: print('enrollments batch: {}'.format(len(enrollments))) | |
return enrollments | |
def enrollment_emails(enrollment): | |
'''Extract unique student emails from enrollments.''' | |
emails = {} | |
for email in enrollment['student'].get('emails', []): | |
emails[email['type']['code']] = email['emailAddress'] | |
return emails | |
def email_repr(emails): | |
if 'CAMP' in emails: | |
return emails['CAMP'] | |
elif 'OTHR' in emails: | |
return emails['OTHR'] | |
return '' | |
def enrollment_id(enrollment): | |
'''Return campus-uid from and enrollment.''' | |
for identifier in enrollment['student']['identifiers']: | |
if identifier['type'] == 'campus-uid': | |
return identifier['id'] | |
def enrollment_status(enrollment): | |
'''Return campus-uid from and enrollment.''' | |
return enrollment['enrollmentStatus']['reason']['code'] | |
def enrollment_name(enrollment): | |
'''Return campus-uid from and enrollment.''' | |
for name in enrollment['student']['names']: | |
if name['type']['description'] == "Preferred": | |
return name['formattedName'] | |
## MAIN | |
# there's an api for this too | |
TERM_FALL_2016 = 2168 | |
TERM_SPRING_2017 = 2172 | |
TERM_SUMMER_2017 = 2176 | |
TERM_FALL_2017 = 2178 | |
TERM_SPRING_2018 = 2182 | |
TERM_SUMMER_2018 = 2185 | |
SUBJECT_AREA = 'STAT' | |
# Headers file contains requests json. Example: | |
# { "Accept": "application/json", | |
# "app_id": "YOUR_SIS_APP_ID", | |
# "app_key": "YOUR_SIS_APP_KEY" } | |
HEADERS_FILE = 'fetch-enrollments.json' | |
parser = argparse.ArgumentParser(description="Print enrolled students' email addresses.") | |
parser.add_argument('-t', dest='term_id', type=int, | |
default=TERM_SPRING_2018, help='SIS Term ID (default=%(default)s)') | |
parser.add_argument('-s', dest='sa_filter', default=SUBJECT_AREA, | |
help='subject area filter (default=%(default)s)') | |
parser.add_argument('-c', dest='course', default='', | |
help='Course number, e.g. c8, 133, 135') | |
parser.add_argument('-e', dest='enrolled', action='store_false', | |
help='Return only enrolled students (default=true)') | |
parser.add_argument('-H', dest='headers_file', default=HEADERS_FILE, | |
help='JSON file containing requests headers') | |
parser.add_argument('-o', dest='output', default="email", | |
help='Output type. ("email", "uid")') | |
parser.add_argument('-v', dest='verbose', action='store_true', help='Be verbose.') | |
parser.add_argument('-d', dest='debug', action='store_true', help='Debug.') | |
args = parser.parse_args() | |
## main | |
# api secrets | |
headers = json.loads(open(args.headers_file).read()) | |
enrollments_uri = "https://apis.berkeley.edu/sis/v2/enrollments" | |
descriptors_uri = enrollments_uri + '/terms/{}/classes/sections/descriptors' | |
sections_uri = enrollments_uri + "/terms/{}/classes/sections/{}" | |
params = { | |
"subject-area-code": args.sa_filter, | |
"catalog-number": args.course, | |
} | |
if args.enrolled: | |
params['enrolled-only'] = 'true' | |
# Retrieve the "section IDs" associated with the course. | |
# This gets both lecture and sections. | |
uri = descriptors_uri.format(args.term_id) | |
sections = get_descriptors(uri, params, headers) | |
# We only care about the lecture. | |
lecture_codes = extract_lecture_codes(sections) | |
# Get the enrollments | |
params['page-size'] = 100 | |
enrollments = [] | |
for lecture_code in lecture_codes: | |
params['page-number'] = 1 | |
uri = sections_uri.format(args.term_id, lecture_code) | |
enrollments += get_enrollments(uri, params, headers) | |
# Get the emails | |
if args.output == 'json': | |
print(json.dumps(enrollments, separators=(',', ': '), indent=4)) | |
else: | |
for e in enrollments: | |
if args.output == 'email': | |
print(email_repr(enrollment_emails(e))) | |
elif args.output == 'uid': | |
print(enrollment_id(e)) | |
elif args.output == 'both': | |
print('{}\t{}\t{}'.format(enrollment_id(e), enrollment_status(e), email_repr(enrollment_emails(e)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment