Skip to content

Instantly share code, notes, and snippets.

@adrianmgg
Last active March 18, 2026 23:18
Show Gist options
  • Select an option

  • Save adrianmgg/f71e77894f9fc67d61fed68b92f9cb7f to your computer and use it in GitHub Desktop.

Select an option

Save adrianmgg/f71e77894f9fc67d61fed68b92f9cb7f to your computer and use it in GitHub Desktop.
import bs4
import itertools
import re
import json
import argparse
import typing
import csv
def cli():
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, epilog=r"""examples:
curl -L -o - 'https://sjsu.edu/classes/schedules/spring-2026.php' | %(prog)s > schedule.json
%(prog)s schedule.html -o schedule.json""")
parser.add_argument('infile', metavar='in', type=argparse.FileType('r'), nargs='?', default='-', help='input file. "-" for stdin (default).')
parser.add_argument('--out', '-o', metavar='OUT', dest='outfile', type=argparse.FileType('w', encoding='utf-8'), default='-', help='output file. "-" for stdout (default).')
return parser.parse_args()
def main(infile: typing.TextIO, outfile: typing.TextIO):
txt = infile.read()
soup = bs4.BeautifulSoup(txt, 'html.parser')
schedule_table = soup.find(id='classSchedule')
assert schedule_table is not None
# some preprocessing
for br in schedule_table.find_all('br'):
br.replace_with('\n')
raw_headers = [th.string for th in schedule_table.thead.tr.find_all('th', recursive=False)]
headers = [re.sub(r"\s+", r"_", str(h)).lower() for h in raw_headers]
class_rows = (
dict(zip(
headers,
(''.join(s.strip(' \t\xa0') for s in td.strings).strip() for td in tr.find_all('td', recursive=False)),
))
for tr in schedule_table.tbody.find_all('tr', recursive=False)
)
outfile.write('[')
for is_first, row in zip(itertools.chain((True,), itertools.repeat(False)), class_rows):
if not is_first: outfile.write(',')
outfile.write('\n\t')
json.dump(row, outfile)
outfile.write('\n]')
if __name__ == '__main__':
main(**vars(cli()))
usage: scrape_sjsu_schedule.py [-h] [--out OUT] [in]
positional arguments:
in input file. "-" for stdin (default).
options:
-h, --help show this help message and exit
--out, -o OUT output file. "-" for stdout (default).
examples:
curl -L -o - 'https://sjsu.edu/classes/schedules/spring-2026.php' | scrape_sjsu_schedule.py > schedule.json
scrape_sjsu_schedule.py schedule.html -o schedule.json
"""
find un-occupied classrooms
e.g.
python freespace.py W '03:00PM-06:00PM' '^MH\d+$'
"""
import argparse
import datetime
from pathlib import Path
import json
import collections
from intervaltree import Interval, IntervalTree
import re
import pprint
data = json.loads(Path('schedule.json').read_text())
relevant_classes = (
c
for c in data
if c['mode_of_instruction'] == 'In Person'
)
def parse_time_interval(s: str) -> tuple[datetime.datetime, datetime.datetime]:
a,b = tuple(
datetime.datetime.strptime(t, '%I:%M%p')
for t in s.split('-')
)
return a,b
def initial_interval():
a,b = parse_time_interval('12:00AM-11:59PM')
t = IntervalTree()
t[a:b] = False
return t
def extract_times(timestr: str):
return map(parse_time_interval, re.findall(r"\d{2}:\d{2}[AP]M-\d{2}:\d{2}[AP]M", timestr))
# room -> ( day -> times )
room_occupations: collections.defaultdict[str, collections.defaultdict[str, IntervalTree[bool]]] = (
collections.defaultdict(
lambda: collections.defaultdict(initial_interval) ))
for c in relevant_classes:
for daychar in c['days']:
for a,b in extract_times(c['times']):
room_occupations[c['location']][daychar][a:b] = True
def foo():
parser = argparse.ArgumentParser()
parser.add_argument('daychar')
parser.add_argument('interval', type=parse_time_interval)
parser.add_argument('room_pattern')
args = parser.parse_args()
assert len(args.daychar) == 1
a,b = args.interval
for room,d in sorted(room_occupations.items(), key=lambda rd: rd[0]):
for day,times in d.items():
if day != args.daychar: continue
if re.match(args.room_pattern, room) is None: continue
relevant_times = times[a:b]
if len(relevant_times) >= 1 and not any(rt.data for rt in relevant_times):
print(room)
foo()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment