Skip to content

Instantly share code, notes, and snippets.

@kmatarese
Last active January 25, 2020 23:25
Show Gist options
  • Save kmatarese/21f9ec5a6b9a81d93caf3b9cc3fe4c8d to your computer and use it in GitHub Desktop.
Save kmatarese/21f9ec5a6b9a81d93caf3b9cc3fe4c8d to your computer and use it in GitHub Desktop.
A script that pulls campsite availability from recreation.gov and emails the result.
from datetime import datetime
from climax import PasswordPrompt
from fake_useragent import UserAgent
from glide import *
import pandas as pd
from tlbx import parse_date
BASE_URL = "https://www.recreation.gov"
AVAILABILITY_URL = BASE_URL + "/api/camps/availability/campground/{camp_id}"
BOOKING_URL = BASE_URL + "/camping/campgrounds/{camp_id}/availability"
def format_date(d):
return datetime.strftime(d, "%Y-%m-%dT00:00:00Z")
class GetCampData(URLExtract):
def run(self, camp_id, start_date, end_date):
print("---- {} {} -> {}".format(camp_id, start_date.date(), end_date.date()))
self.update_downstream_context(dict(camp_id=camp_id))
super().run(
AVAILABILITY_URL.format(camp_id=camp_id),
data_type="json",
params=dict(
start_date=format_date(start_date), end_date=format_date(end_date)
),
headers={"User-Agent": UserAgent().random},
)
class FilterAvailability(Node):
def run(self, data, site_name=None, site_type=None, **kwargs):
camp_id = kwargs["camp_id"] # context from upstream
sites = []
available = {}
for site in data.get("campsites", {}).values():
if site_name and site["site"] != site_name:
continue
if site_type and site["campsite_type"] != site_type:
continue
for date, status in site["availabilities"].items():
if status == "Available":
sites.append(
dict(
date=date.split("T")[0],
site_name=site["site"],
site_type=site["campsite_type"],
)
)
print("{} sites available".format(len(sites)))
if sites:
available["camp_id"] = camp_id
available["url"] = BOOKING_URL.format(camp_id=camp_id)
available["sites"] = sites
self.push(available)
class FormatHTML(Node):
def run(self, data):
output = ""
for row in data:
output += "Camp ID: {camp_id}<br>URL: {url}<br>\n".format(**row)
df = pd.DataFrame.from_records(row["sites"])
html_content = df.to_html(index=False)
output += html_content + "<br>\n"
if output:
self.push(output)
glider = Glider(
GetCampData("camp")
| FilterAvailability("filter")
| Reduce("reduce")
| FormatHTML("html")
| EmailLoad("email", subject="Camp Sites Available", attach_as="html")
)
@glider.cli(
Arg("glide_data", metavar="camp_id", nargs="+", type=int, help="Camp IDs"),
# All of these args already appear with node name prefixed by default, but
# adding them here for clarity and more concise names on the command line.
Arg("--start_date", required=True, type=parse_date, help="Start date [YYYY-MM-DD]"),
Arg("--end_date", required=True, type=parse_date, help="End date [YYYY-MM-DD]"),
# Note: could utilize glide's ConfigContext to read from a file instead
Arg("--host", required=True, help="Email host"),
Arg("--port", required=True, type=int, help="Email port to connect to"),
Arg("--frm", required=True, help="Email from address"),
Arg("--to", required=True, help="Email destination address(es), comma separated"),
Arg("--username", required=True, help="Email account username"),
Arg("password", action=PasswordPrompt, help="Email password"),
)
def main(glide_data, node_contexts, **kwargs):
glider.consume(glide_data, **node_contexts)
if __name__ == "__main__":
main()
@kmatarese
Copy link
Author

kmatarese commented Jan 24, 2020

To run this script, install the following which should cover all dependencies:

$ pip install glide fake-useragent

The example below pulls availability for two popular sites in Yosemite from 6/1/2020 to 6/10/2020. The camp IDs are Recreation.gov's facility IDs that appear in the URL for a particular campground. Fill in the blanks for your email info. You will be prompted for your password. The "frm" param will likely be the same as the "username".

$ python glide_campsite_example.py 232449 232447 --start_date 2020-06-01 --end_date 2020-06-10 --host smtp.gmail.com --port 587 --frm [email protected] --to [email protected] --username [email protected]

You could also easily adjust the script to set some values as defaults or read them from a config file to simplify the command line. More info on glide here.

NOTE: Please don't abuse this script or use it against the terms of recreation.gov.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment