ActiveSoftwareAutomation

Stanford Dining Recommender

Automated daily dining recommendations via Discord. Python, OpenAI, GitHub Actions.

RoleSolo Developer
Timeline2026
UpdatedMay 11, 2026
RunsDaily via GitHub Actions
DeliveryDiscord webhook
ScoringDeterministic + OpenAI
FallbackDeterministic ranking if AI fails
Stanford Dining Recommender cover image
Overview

A scheduled Python pipeline that fetches Stanford's dining hall menus each day, scores every option against my actual dietary preferences, and sends a concise recommendation to Discord — best hall, backup, recommended plate, and what to skip. Built because manually clicking through five dining halls to find the best meal was a daily annoyance I could automate once and solve forever.


The problem

Stanford's dining system doesn't expose a clean public API. Getting reliable daily data meant working with a form-based web interface: select date, select meal, select hall, extract results. Doing this for every combination at every meal time, then turning raw menu text into a useful recommendation, required a pipeline that could handle scraping failures gracefully and still produce a usable output even if one layer broke.

Constraints
  • No public JSON API — scraper has to navigate Stanford's R&DE menu form for every hall and meal combination
  • Recommendation needs to reflect my actual preferences, not a generic 'healthy eating' heuristic
  • Fallback to deterministic scoring if the AI layer fails — the pipeline should never silently produce nothing
  • Runs on GitHub Actions schedule; must be stateless, fast, and cheap to operate
Design decisions
  • Deterministic scoring runs first, before the model sees anything. The AI layer refines and explains the output — it doesn't make the core decision alone.
  • Saved menu data to structured JSON at each stage so failures are inspectable and the scraper can be debugged without re-running everything
  • Discord webhook for delivery — always-on, no app to check, recommendation arrives at meal time
  • Explicit penalty and reward lists derived from my actual preferences, not a generic nutritional API. Fried food, processed meat, seafood, and egg-forward dishes are penalized. Whole foods, legumes, quality protein, and vegetables score well.
Build process
  1. 01

    Built the scraper to navigate Stanford's R&DE dining form: select date, select meal period, cycle through each hall, extract and clean menu items

  2. 02

    Structured output as JSON at each stage for inspectability

  3. 03

    Wrote the dietary scoring engine with explicit lists: high-value foods, disqualifying foods, and neutral fillers

  4. 04

    Integrated OpenAI to convert the ranked menu data into a readable recommendation: best hall, backup, plate suggestion, foods to avoid, confidence, and short reasoning

  5. 05

    Added fallback logic: if the AI layer fails or returns malformed output, the system sends the deterministic ranking directly

  6. 06

    Deployed as a GitHub Actions cron job — runs automatically at meal times, no server required


Result

Running daily. Gets the recommendation right most of the time and consistently surfaces options I'd have missed by not checking every hall manually. The fallback has triggered a handful of times when the OpenAI call hit a rate limit; the deterministic output was still useful.

What went wrong / what I learned
  • Scraping form-based interfaces is fragile. Small changes to the page structure break the selector. Worth building in assertions that catch silent failures before they produce bad data.
  • The deterministic scoring layer ended up being more useful than expected — it's fast, inspectable, and forces you to articulate your actual preferences rather than leaving it vague for the model.
  • GitHub Actions cron is a surprisingly good host for lightweight pipelines. Stateless, free at low frequency, and the logs are right there when something breaks.

Tools & Methods

PythonBeautifulSoupOpenAI APIGitHub ActionsDiscord webhooksJSON

Specs

Runs
Daily via GitHub Actions
Delivery
Discord webhook
Scoring
Deterministic + OpenAI
Fallback
Deterministic ranking if AI fails