-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.py
161 lines (127 loc) · 5.6 KB
/
index.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
"""
Copyright (c) 2016 Stepan Fedorko-Bartos, Ceegan Hale
Under MIT License - https://github.com/Step7750/ScheduleStorm/blob/master/LICENSE.md
This file is a resource for Schedule Storm - https://github.com/Step7750/ScheduleStorm
"""
import uni
from rmp import RateMyProfessors
import json
import inspect
import time
import logging
import sys
import falcon
from threading import Lock
from wsgiref import simple_server
import hashlib
# Store the threads for each uni
uniThreads = {}
# Logging
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
log = logging.getLogger("main")
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("wsgiref").setLevel(logging.WARNING)
def loadSettings():
with open("settings.json") as settingFile:
return json.load(settingFile)
settings = loadSettings()
class v1Unis():
"""
Retrieves list of Unis
"""
def on_get(self, req, resp):
responsedict = {}
for uni in list(uniThreads.keys()):
responsedict[uni] = {"terms": uniThreads[uni].getTerms(),
"locations": uniThreads[uni].getLocations(),
"name": settings["Universities"][uni]["fullname"],
"rmp": settings["Universities"][uni]["rmpid"],
"scraping": uniThreads[uni].isScraping}
str_response = json.dumps(responsedict).encode('utf-8')
# set the etag and body
resp.etag = "W/" + hashlib.sha1(str_response).hexdigest()
resp.body = str_response
class v1GetAllUniTermSubjects():
"""
Retrieves all subjects and classes for a given Uni
"""
def on_get(self, req, resp, uni, term):
# The term must be a string since the threads represent them as such
if uni in uniThreads and term in uniThreads[uni].getTerms():
if uniThreads[uni].isScraping:
# we don't want to return data while scraping, send error (configure nginx to send stale data if it can)
resp.status = falcon.HTTP_500
resp.body = json.dumps(
{"error": "We're currently scraping this university, please check back in a couple minutes!"}
).encode('utf-8')
else:
# Get course/subject list
subject_list = json.dumps(uniThreads[uni].getSubjectListAll(term), sort_keys=True).encode('utf-8')
# set the etag and body
resp.etag = "W/" + hashlib.sha1(subject_list).hexdigest()
resp.body = subject_list
else:
# Couldn't find the uni or term, send error
resp.status = falcon.HTTP_400
resp.body = json.dumps(
{"error": "The specified university or term was not found"}
).encode('utf-8')
if __name__ == '__main__':
# Instantiate the unis
# Get the modules of uni
unimemebers = inspect.getmembers(uni)
rmpids = []
# lock for file synchronization
lock = Lock()
log.info("Instantiating University Threads")
# Foreach university in settings
for university in settings["Universities"]:
# Get the settings
unisettings = settings["Universities"][university]
# Set the key and lock
unisettings["uniID"] = university
unisettings["lock"] = lock
# Only instantiate if they have it enabled in settings
if "enabled" in unisettings and unisettings["enabled"]:
# Check if rmpid is set, if so, add it to the rmpids list
if "rmpid" in unisettings:
rmpids.append(unisettings["rmpid"])
foundClass = False
# Find the module of this uni
for member in unimemebers:
if member[0] == university:
# Now find the class in the module
uniclasses = inspect.getmembers(member[1])
# Iterate the classes
for uniclass in uniclasses:
if uniclass[0] == university:
# Found the class, it must be the same name as the key for this Uni (ex. UCalgary)
uniThreads[university] = uniclass[1](unisettings)
log.info("Instantiated " + university + "'s thread")
foundClass = True
if not foundClass:
log.error("We couldn't find the class to instantiate for", university)
log.info("Starting University Threads")
# Start each Uni thread
for uniThread in uniThreads:
if "scrape" not in settings["Universities"][uniThread]:
log.error(uniThread + " must have a scrape attribute!")
else:
if settings["Universities"][uniThread]["scrape"] is True:
# scraping is enabled
log.info("Starting " + uniThread + "'s thread")
uniThreads[uniThread].start()
# Start up the RateMyProfessors scraper if there is at least one rmp id
if len(rmpids) > 0 and "rmpinterval" in settings:
log.info("Starting RMP scraper")
rmpthread = RateMyProfessors(rmpids, settings["rmpinterval"])
rmpthread.start()
# Run the Falcon API server
app = falcon.API()
# Add the routes
app.add_route('/v1/unis', v1Unis())
app.add_route('/v1/unis/{uni}/{term}/all', v1GetAllUniTermSubjects())
# It is highly recommended to put this API behind a proxy such as nginx with heavy caching
log.info("Setting up API server on port " + str(settings["port"]))
httpd = simple_server.make_server('0.0.0.0', settings["port"], app)
httpd.serve_forever()