-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdatasource.py
executable file
·317 lines (254 loc) · 12.5 KB
/
datasource.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
'''datasource.py
CS 257 Python Web App, Fall 2013, Jeff Ondich
Jiatao Cheng, Erin Wilson, and Adam Canady
This module defines how to connect to our datasource and various associated
functionalities like getting metrics from various dimensions.
Some of the following code is from Jeff Ondich's psycopg2-demo.py
'''
import psycopg2
class DataSource:
def __init__(self):
# Start with the database login info
self.database = 'canadya'
self.user = 'canadya'
self.password = 'star925propane'
self.connect()
def connect(self):
# Login to the database
try:
self.connection = psycopg2.connect(database=self.database,
user=self.user,
password=self.password)
self.cursor = self.connection.cursor()
except Exception, e:
raise e
def close(self):
self.connection.close()
# Function to generate queries based on preferences indicated by the user in web form
def get_rooms_by_preference(self, user_input_building = "",
user_input_occupancy = 0,
user_input_environment = ""):
self.query = """SELECT MIN(roomdraw_num), building, room
FROM rooms
LEFT JOIN buildings ON rooms.building_id = buildings.id
LEFT JOIN number_map ON avg_draw_number = number_map.db_num\n"""
# The if/else clauses are used to determine if we need to create
# a new WHERE block or add to an existing one
if user_input_occupancy > 0:
if "WHERE" in self.query:
self.query += " AND "
else:
self.query += " WHERE "
self.query += "occupancy = %d" % user_input_occupancy
if user_input_environment == "sub_free":
if "WHERE" in self.query:
self.query += " AND "
else:
self.query += " WHERE "
self.query += "sub_free = 't'"
if user_input_environment == "quiet":
if "WHERE" in self.query:
self.query += " AND "
else:
self.query += " WHERE "
self.query += "quiet = 't'"
if user_input_building:
if "WHERE" in self.query:
self.query += " AND "
else:
self.query += " WHERE "
self.query += "building = \'%s\'" % user_input_building
self.query += "GROUP BY building, room;"
self.cursor.execute(self.query)
return self.cursor.fetchall()
# Function that takes user preferences as input and returns lists of rooms
# with certain categorical probability given the user's draw number
# Stretch: not likely to get this room
# (rooms with average draws < 30 of you)
# Target: number is right in range of getting this room
# (rooms with average draws within +/- 30 of you)
# Safety: very likely to get this room
# (rooms with average draws > 30 of you)
def get_rooms_like_this(self, converted_draw_number,
user_input_environment,
user_input_occupancy,
user_input_building):
''' Returns a list of rooms in the format:
[building, room] '''
rooms = self.get_rooms_by_preference(user_input_building, user_input_occupancy, user_input_environment)
# calculate span of user's target rooms
upper_target_bound = converted_draw_number + 30
lower_target_bound = converted_draw_number - 30
stretch = []
target = []
safety = []
# Sort rooms into categories of likeliness
if not rooms:
raise Exception("No rooms found that match your preferences... :(")
for room in rooms:
if room[0] < lower_target_bound:
stretch.append(room)
elif room[0] >= lower_target_bound and room[0] < upper_target_bound:
target.append(room)
elif room[0] >= upper_target_bound:
safety.append(room)
return stretch, target, safety
# Function to help a user avoid an enemy because we are on such a small campus!
def get_rooms_far_away(self, converted_enemy_number,
converted_draw_number):
''' Returns a list of rooms in the format:
[building, room, occupancy, sub_free, quiet]
that are at least 250 meters away from each other.'''
# Find enemy's target room where "target room" is defined as the
# room whose average draw number is closest to the enemy's draw number
self.query = """SELECT building, room, occupancy, sub_free, quiet, geo_lat, geo_long, abs(avg_draw_number - %d) as draw_distance
FROM rooms
LEFT JOIN buildings ON rooms.building_id = buildings.id
ORDER BY draw_distance ASC;""" % (converted_enemy_number)
self.cursor.execute(self.query)
enemy_list = self.cursor.fetchall()
if enemy_list:
enemy_target_room = enemy_list[0]
enemy_lat = enemy_target_room[5]
enemy_long = enemy_target_room[6]
else:
raise Exception('Something went wrong while determining your enemy\'s best room!')
# Find rooms outside a 250m radius from enemy's target room
self.query = """SELECT MIN(roomdraw_num), building, room, occupancy, sub_free, quiet, avg_draw_number
FROM rooms
LEFT JOIN buildings ON rooms.building_id = buildings.id
LEFT JOIN number_map ON avg_draw_number = number_map.db_num
WHERE sqrt(power((geo_lat - %s),2) + power((geo_long - %s),2))*79208 > 250
GROUP BY building, room, occupancy, sub_free, quiet, avg_draw_number;""" % (enemy_lat, enemy_long)
self.cursor.execute(self.query)
results = self.cursor.fetchall()
# calculate span of user's target rooms
upper_target_bound = converted_draw_number + 30
lower_target_bound = converted_draw_number - 30
stretch = []
target = []
safety = []
# Sort rooms into categories of likeliness
if not results:
Exception('No rooms found :(')
for room in results:
if room[6] < lower_target_bound:
stretch.append(room[:6])
elif room[6] >= lower_target_bound and room[6] < upper_target_bound:
target.append(room[:6])
elif room[6] >= upper_target_bound:
safety.append(room[:6])
return enemy_target_room, stretch, target, safety
# Function to help a user find a room near a favorite location
def get_rooms_near_location(self, converted_draw_number,
favorite_location):
# Get favorite_location latlon
self.query = """SELECT building, geo_lat, geo_long
FROM buildings
WHERE building = \'%s\'""" % (favorite_location)
self.cursor.execute(self.query)
location_list = self.cursor.fetchall()
if location_list:
favorite_location = location_list[0]
fav_location_lat = favorite_location[1]
fav_location_long = favorite_location[2]
else:
raise Exception('Something went wrong while determining your favorite location!')
# Find room within 250m radius of favorite location
self.query = """SELECT MIN(roomdraw_num), building, room, occupancy, sub_free, quiet, avg_draw_number
FROM rooms
LEFT JOIN buildings ON rooms.building_id = buildings.id
LEFT JOIN number_map ON avg_draw_number = number_map.db_num
WHERE sqrt(power((geo_lat - %s),2) + power((geo_long - %s),2))*79208 < 250
GROUP BY building, room, occupancy, sub_free, quiet, avg_draw_number;""" % (fav_location_lat, fav_location_long)
self.cursor.execute(self.query)
results = self.cursor.fetchall()
# calculate span of user's target rooms
upper_target_bound = converted_draw_number + 30
lower_target_bound = converted_draw_number - 30
stretch = []
target = []
safety = []
# Sort rooms into categories of likeliness
for room in results:
if room[6] < lower_target_bound:
stretch.append(room[:6])
elif room[6] >= lower_target_bound and room[6] < upper_target_bound:
target.append(room[:6])
elif room[6] >= upper_target_bound:
safety.append(room[:6])
else:
Exception('No rooms found :(')
return stretch, target, safety
# General function to retrieve rooms within a given range of average draw numbers
def get_rooms_in_range(self, lower, upper):
''' Returns a list of rooms in the format:
[building, room, occupancy, sub_free, quiet] '''
self.query = """SELECT MIN(roomdraw_num), building, room, occupancy, sub_free, quiet
FROM rooms
LEFT JOIN buildings ON rooms.building_id = buildings.id
LEFT JOIN number_map ON avg_draw_number = number_map.db_num
WHERE avg_draw_number > %d and avg_draw_number < %d
GROUP BY building, room, occupancy, sub_free, quiet;""" % (lower, upper)
self.cursor.execute(self.query)
return self.cursor.fetchall()
# Function that reports the probability that you can get a specific room
# (Stretch, Target, or Safety)
def specific_room_possibility(self, converted_draw_number,
room,
building):
''' Returns one of ["Stretch", "Target", "Safety", "Draw Not Available"] '''
self.query = """SELECT avg_draw_number
FROM rooms
LEFT JOIN buildings ON rooms.building_id = buildings.id
WHERE building = \'%s\' and room = %s;""" % (building, room)
self.cursor.execute(self.query)
result = self.cursor.fetchall()
upper_target_bound = converted_draw_number + 30
lower_target_bound = converted_draw_number - 30
# Find the category that the user's preferred room falls into
if len(result) > 0:
avg_draw_number = result[0][0]
if avg_draw_number < lower_target_bound:
return "Stretch"
elif avg_draw_number >= lower_target_bound and avg_draw_number <= upper_target_bound:
return "Target"
elif avg_draw_number > upper_target_bound:
return "Safety"
else:
raise Exception('Number Not Available')
else:
return "Draw Not Available"
# Function convert an assigned draw number to a number usable by the database
# (1001 = 1, 1002 = 2, etc)
# Note: because each class starts at _001 and ends around _510, real draw number 2001
# was assigned 511 in the database so that all draw numbers are continuous across classes.
# This is important for being able to accurately average draw numbers
def convert_number(self, user_input):
self.query = """SELECT db_num
FROM number_map
WHERE roomdraw_num = %d;""" % (user_input)
self.cursor.execute(self.query)
result = self.cursor.fetchall()
if len(result) > 0:
return result[0][0]
else:
raise Exception('Invalid Room Draw Number') # Raised if number not between 1000 and 4000
# def get_rooms_by_number(self, number):
# self.query = 'SELECT draw_number, building, room_number, occupancy FROM roomdraw WHERE draw_number > %s' % number
# self.cursor.execute(self.query)
# return self.cursor.fetchall()
''' Tests '''
if __name__ == "__main__":
db = DataSource()
print "Specific Room Possibility:"
print db.specific_room_possibility(182, 213, "Cassat Hall")
print db.query
print
print "Get Rooms By Preference:"
print db.get_rooms_by_preference(user_input_building = "Cassat Hall", user_input_occupancy = 1, user_input_environment = "sub_free")
print db.query
print
print "Get Rooms In Range:"
print db.get_rooms_in_range(462, 402)
print db.query