Created
March 15, 2021 15:09
-
-
Save alextremblay/087810b572c50a9981e16d3ad44833be to your computer and use it in GitHub Desktop.
Ontario Parks Availability
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
from pathlib import Path | |
import pickle | |
from typing import Union | |
import csv | |
from uuid import uuid4 | |
import requests | |
import arrow | |
target = 'Aug 15 2021' | |
days = 7 | |
start = arrow.get(target, 'MMM DD YYYY') | |
end = start.shift(days=days) | |
def timecode(time: arrow.Arrow, date_only=True): | |
if date_only: | |
return f'{time.format("YYYY-MM-DD")}T00:00:00.000Z' | |
#else: | |
return time.format("YYYY-MM-DDTHH:mm:ss.SSS[Z]") | |
def fetch_map_id(map_id): | |
url = 'https://reservations.ontarioparks.com/api/maps/mapdatabyid' | |
payload = { | |
"mapId": map_id, | |
"cartUid": str(uuid4()), | |
"bookingUid": str(uuid4()), | |
"cartTransactionUid": str(uuid4()), | |
"bookingCategoryId": 0, | |
"startDate": f'{start.format("YYYY-MM-DD")}T00:00:00.000Z', | |
"endDate": f'{end.format("YYYY-MM-DD")}T00:00:00.000Z', | |
"isReserving": True, | |
"getDailyAvailability": False, | |
"partySize": 4, | |
"filterData": "[]", | |
"equipmentCategoryId": -32768, | |
"subEquipmentCategoryId": -32768, | |
"boatLength": None, | |
"boatDraft": None, | |
"boatWidth": None, | |
"generateBreadcrumbs": False, | |
"resourceAccessPointId": None | |
} | |
current_time = arrow.utcnow().format("YYYY-MM-DDTHH:mm:ss.SSS[Z]") | |
res = requests.post( | |
f'{url}?seed={current_time}', | |
json=payload | |
) | |
data = res.json() | |
return data | |
availability_map = { | |
0:"Available", | |
1:"Unavailable", | |
2:"Unavailable", | |
3:"Unavailable", | |
4:"Unavailable", | |
6:"No Availability For Selected Booking Category", | |
7:"Partially Available", | |
} | |
def get_info(map_id, names = None): | |
"fetch a map by map_id, recursively fetch every mapped linked to by that map, and availability counts for every campsite linked to in any of those maps" | |
data = fetch_map_id(map_id) | |
res = {} | |
sub_sections = data.get('mapLinkLocalizedValues') | |
sites = data.get('resourceAvailabilityMap') | |
if sub_sections: | |
for sub_id in sub_sections: | |
name = sub_sections[sub_id][0]['title'] | |
if names: | |
new_names = names + [name] | |
else: | |
new_names = [name] | |
res[name] = get_info(sub_id, new_names) | |
elif sites: | |
total = len(sites) | |
avail = 0 | |
partial = 0 | |
for site in sites.values(): | |
if site and site[0]['availability'] == 0: | |
avail += 1 | |
if site and site[0]['availability'] == 7: | |
partial += 1 | |
print('fetched', ' > '.join(names)) | |
return (avail, partial, total) | |
else: | |
raise Exception("How did we get here?") | |
return res | |
def get_count(sub: Union[dict, tuple]): | |
if isinstance(sub, tuple): | |
return sub | |
#else: | |
avail, partial, total = 0,0,0 | |
for k, v in sub.items(): | |
subavail, subpartial, subtotal = get_count(v) | |
avail += subavail | |
partial += subpartial | |
total += subtotal | |
return avail, partial, total | |
def get_pairs(regions_: dict): | |
for region_name, region in regions_.items(): | |
for park_name, park in region.items(): | |
avail, partial, total = get_count(park) | |
yield f'{region_name} > {park_name}', avail, partial, total | |
def summary(regions_: dict): | |
avail_symbol = '#' | |
partial_symbol = '-' | |
unavail_symbol = ' ' | |
print(f'Legend: "{avail_symbol}" = Available, "{partial_symbol}" = Partially available, "{unavail_symbol}" = Unavailable') | |
for name, avail, partial, total in get_pairs(regions_): | |
avail_per = avail / total | |
bar_length = 50 | |
avail_per = avail / total | |
partial_per = partial / total | |
bar = int(avail_per*bar_length)*avail_symbol | |
bar += int(partial_per*bar_length)*partial_symbol | |
bar = bar.ljust(bar_length, unavail_symbol) | |
print(f'[{bar}] {name}: Available: {avail}/{total}, Partially Available: {partial}/{total}') | |
def write_csv(regions_): | |
with Path('res.csv').open('w') as f: | |
writer = csv.writer(f) | |
writer.writerow(['Park Name', 'Available Campsites', 'Partially Available Campsites', 'Total Campsites']) | |
for row in get_pairs(regions_): | |
writer.writerow(row) | |
print("Results written to res.csv") | |
regions = get_info(-2147483464) | |
Path('res.pkl').write_bytes(pickle.dumps(regions)) | |
#regions = pickle.loads(Path('res.pkl').read_bytes()) | |
summary(regions) | |
write_csv(regions) | |
print("Done!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment