Generate the ORFE Thesis Symposium schedule from a corresponding web site and generate a nicely formatted, paginated PDF or JSON output.
- Scrapes schedule data from https://symposium.orfe.princeton.edu
- Handles Cloudflare protection using Playwright
- Extracts room information, advisors, graders, and presentation schedules
- Generates paginated PDF with formatted tables
- Optional JSON output for data processing
- Comprehensive unit tests
- Python 3.8+
- Playwright (for browser automation)
- BeautifulSoup4 (for HTML parsing)
- ReportLab (for PDF generation)
- Clone or download the repository
- Install Python dependencies:
pip install -r requirements.txt
- Install Playwright browsers:
python -m playwright install chromium
python scrape_schedule.pyThis creates symposium_schedule.pdf with the formatted schedule, keeping each room's information together on pages.
python scrape_schedule.py --allow-breaksThis allows page breaks that might separate room titles from their content (not recommended).
python scrape_schedule.py --show-headersInclude column headers (Time, Presenter) in the tables.
python scrape_schedule.py --jsonThis outputs the schedule data as JSON to stdout and saves it to symposium_schedule.json.
python scrape_schedule.py --no-titleExcludes the title header from the PDF output.
python scrape_schedule.py --qr-codesIncludes QR codes for each room that link to the room's anchor on the webpage.
python scrape_schedule.py --gridCreates symposium_schedule_grid.pdf with all rooms side by side in a landscape table layout.
python -m unittest test_scrape.py -v- Title page with symposium name
- Separate sections for each room
- Advisor and grader information
- Table with Time and Presenter columns
- Automatic pagination
{
"001": {
"advisors": "ORFE Advisors: Dr. Smith, Dr. Johnson",
"graders": "PhD Candidate Graders: Alex Chen",
"schedule": [
["9:00 am – 9:15 am", "John Anderson"],
["9:15 am – 9:30 am", "Sarah Mitchell"]
]
}
}The project includes unit tests to ensure:
- At least one room is scraped
- Each room has required data structure
- Schedule items are properly formatted
- No regression in parsing logic
Run tests with:
python -m unittest test_scrape.py- requests
- beautifulsoup4
- reportlab
- playwright
- pytest (optional, for testing)
This project is for educational purposes. Please respect the website's terms of service and Princeton University's policies.
Automatically generates PDF and JSON outputs. Triggers on:
- Push to main — regenerates on every commit
- Scheduled (every 30 minutes) — polls the source website for changes and regenerates if the schedule content has been updated
- Manual dispatch — run from the Actions tab with configurable options:
- Show headers: Include column headers in the PDF table
- Allow breaks: Allow page breaks within room sections
- Include title: Include the title in PDF output
- QR codes: Include QR codes linking to room anchors
- Grid PDF: Generate landscape grid PDF with all rooms side by side
- Force generate: Bypass change detection and regenerate regardless
The workflow hashes the scraped schedule data and compares it to a cached hash from the previous run. If no changes are detected, the release step is skipped. Use Force generate to bypass this check.
The workflow produces artifacts and a GitHub Release (latest tag) containing:
symposium_schedule.pdf— Formatted PDF with QR codessymposium_schedule_grid.pdf— Landscape grid PDF with all rooms side by sidesymposium_schedule.json— Structured JSON data
Runs the full test suite on pushes to main and pull requests. Tests can also be run locally via Docker:
docker build -f Dockerfile.test -t symposium-test . && docker run --rm symposium-testThe pages/ directory contains a public display page designed for conference room TVs (1080p and 4K). It is deployed automatically to GitHub Pages.
Once GitHub Pages is enabled, the display is available at:
| URL | View |
|---|---|
display.html?floor=ground |
Ground floor rooms (001, 003, 008) |
display.html?floor=1 |
1st floor rooms (101–125) |
display.html?rooms=001,003 |
Specific rooms only |
display.html |
All rooms |
Add &highlight=false to disable current-talk highlighting.
- On GitHub Pages,
display.htmlfetches thesymposium_schedule.jsonthat the generate workflow publishes to thelatestrelease. The Pages workflow downloads this JSON and deploys it alongside the display page. - The page refreshes data every 5 minutes. The JSON is updated every 20 minutes by the generate workflow.
- All sizing uses
vwunits so the layout scales identically on 1080p and 4K displays. On smaller screens (laptops), the page scrolls.
For environments that inject CSS on top of the original symposium URL:
override-base.css— core overrides (always required)override-floor-ground.css— show only ground floor roomsoverride-floor-1.css— show only 1st floor rooms
cd pages
python3 ../serve.py 8000Or serve with any static file server — the page falls back to /proxy/schedule when not on GitHub Pages or princeton.edu.