Source code for penn.studyspaces

import requests
import datetime
import json
import pytz
import re
import six

from bs4 import BeautifulSoup


BASE_URL = "https://libcal.library.upenn.edu"


[docs]class StudySpaces(object): """Used for interacting with the UPenn library GSR booking system. Usage:: >>> from penn import StudySpaces >>> s = StudySpaces() """ def __init__(self): pass
[docs] def get_buildings(self): """Returns a list of building IDs, building names, and services.""" soup = BeautifulSoup(requests.get("{}/spaces".format(BASE_URL)).content, "html5lib") options = soup.find("select", {"id": "lid"}).find_all("option") return [{"id": int(opt["value"]), "name": str(opt.text), "service": "libcal"} for opt in options]
[docs] @staticmethod def parse_date(date): """Converts library system dates into timezone aware Python datetime objects.""" date = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S") return pytz.timezone("US/Eastern").localize(date)
[docs] @staticmethod def get_room_id_name_mapping(building): """ Returns a dictionary mapping id to name, thumbnail, and capacity. """ data = requests.get("{}/spaces?lid={}".format(BASE_URL, building)).content.decode("utf8") # find all of the javascript room definitions out = {} for item in re.findall(r"resources.push\(((?s).*?)\);", data, re.MULTILINE): # parse all of the room attributes items = {k: v for k, v in re.findall(r'(\w+?):\s*(.*?),', item)} # room name formatting title = items["title"][1:-1] title = title.encode().decode("unicode_escape" if six.PY3 else "string_escape") title = re.sub(r" \(Capacity [0-9]+\)", r"", title) # turn thumbnail into proper url thumbnail = items["thumbnail"][1:-1] if thumbnail: thumbnail = "https:" + thumbnail room_id = int(items["eid"]) out[room_id] = { "name": title, "thumbnail": thumbnail or None, "capacity": int(items["capacity"]) } return out
[docs] def get_rooms(self, building, start, end): """Returns a dictionary matching all rooms given a building id and a date range.""" if start.tzinfo is None: start = pytz.timezone("US/Eastern").localize(start) if end.tzinfo is None: end = pytz.timezone("US/Eastern").localize(end) mapping = self.get_room_id_name_mapping(building) room_endpoint = "{}/process_equip_p_availability.php".format(BASE_URL) data = { "lid": building, "gid": 0, "start": start.strftime("%Y-%m-%d"), "end": (end + datetime.timedelta(days=1)).strftime("%Y-%m-%d"), "bookings": [] } resp = requests.post(room_endpoint, data=json.dumps(data), headers={'Referer': "{}/spaces?lid={}".format(BASE_URL, building)}) rooms = {} for row in resp.json(): room_id = int(row["resourceId"][4:]) if room_id not in rooms: rooms[room_id] = [] room_start = self.parse_date(row["start"]) room_end = self.parse_date(row["end"]) if start <= room_start <= end: rooms[room_id].append({ "start": room_start.isoformat(), "end": room_end.isoformat(), "available": row["status"] == 0 }) out = [] for k, v in rooms.items(): item = { "room_id": k, "times": v } if k in mapping: item.update(mapping[k]) out.append(item) return out