π½οΈ BiteTime
Personalized dining recommendations for SJSU students β powered by Bob AI.
π Live site: sjsufood.sagg.in Β· Built for the IBM AI Challenge @ SpartHill Hacks 2026: AI for Sustainable Impact
Inspiration
Every day, SJSU students walk into The Dining Commons without knowing what to eat. The currently offered menu is a flat list of food with no personal context. We wanted to solve the daily "what should I eat?" friction for our students by building something that learns their taste. Sending them a notification of food they might be interested in on the menu. This way they have a source to know whether they want to spend a meal swipe before actually getting into the cafeteria.
What it does
BiteTime is a personalized dining recommendation web app for SJSU students:
| Feature | Description |
|---|---|
| π Live daily menu | Browse breakfast, lunch, and dinner for any date, pulled live from the Dine On Campus API by SpartanEat |
| π Rate dishes | Thumbs up / down on any menu item, saved to your Google-authenticated account |
| β¨ AI recommendations | Unrated dishes you're likely to enjoy are flagged "You might like this" and floated to the top |
| π§ Daily email digest | Each morning you receive a personalized shortlist of top picks before you even leave for campus |
How we built it
Stack
- Backend: Django 6.1 Β· Python 3.12 Β· SQLite
- Frontend: Server-rendered Django templates Β· Tailwind CSS
- AI: Bob Ai Β· Claude Β· Codex
- ML:
fastembedΒ·sentence-transformers/all-MiniLM-L6-v2 - Auth: Google OAuth 2.0 (server-side token verification)
- Email: Django email backend Β· scheduled
send_recommendationsmanagement command - Deployment: Gunicorn Β· sjsufood.sagg.in
Architecture
Dine On Campus API
β
βΌ
lib/menu_scraper.py βββ seed_menu management command (runs per meal period)
β
βΌ
MenuEntry / Food (Django ORM)
β
ββββΆ menu view βββΆ lib/food_recommender.py βββΆ "You might like this" badges
β β²
β UserRating (thumbs up / down per user)
β
ββββΆ send_recommendations βββΆ daily email digest
Recommendation engine
lib/food_recommender.py embeds food names as vectors using all-MiniLM-L6-v2 and computes cosine similarity between a candidate dish and everything a user has liked or disliked:
- Exact match short-circuits the model instantly.
- Best-like similarity must exceed a threshold (
0.55). - Likeβdislike margin must be positive enough (
0.05) to avoid recommending something that's actually closer to a disliked dish.
This means "Chicken Teriyaki Bowl" gets recommended to someone who liked "Orange Chicken" β purely from dish-name semantics, no ingredient lists required.
Challenges we ran into
- No public API docs β The Dine On Campus site is a JavaScript SPA. We reverse-engineered its JSON endpoints, including a multi-step flow (fetch period IDs β fetch menu) and browser-like headers to avoid being rate-limited.
- Embedding model cold start β
fastembeddownloads the model on first use. A failed download or inference timeout must never hide already-saved user votes, so the recommendation step is wrapped in a silent fallback. - Idempotent email delivery β Preventing duplicate emails required a
RecommendationDeliverymodel with aUniqueConstrainton(gmail, recommendation_date)and carefulget_or_create+sent_atgating. - Threshold tuning β Finding the right cosine similarity threshold and like-vs-dislike margin to surface genuinely relevant dishes without over-recommending took several iterations.
Accomplishments that we're proud of
- π€ A working AI recommendation pipeline driven entirely by food name embeddings β no nutrition data, no ingredient lists β that still produces intuitive results.
- π A robust, idempotent menu scraper that handles API quirks and can be safely re-run for any date and meal period.
- π¨ A clean, accessible UI with date navigation, meal period tabs, and per-item vote toggles β server-rendered, zero JavaScript frameworks.
- π¬ A proactive daily email pipeline with deduplication, error logging, and a full audit trail of every recommendation sent.
What we learned
- Semantic text embeddings are a surprisingly powerful signal for food similarity β treating dish names as natural language and measuring their proximity in embedding space works well enough to drive real recommendations.
- Reverse-engineering a JS-heavy SPA's internal API (session cookies, multi-step period IDs, CORS headers) takes patience but produces a far more stable integration than HTML scraping.
- Building idempotent pipelines (menu seeding, email delivery) from the start saves a lot of pain when things need to be recovered or replayed.
What's next for BiteTime
- [ ] Nutrition-aware filtering β incorporate dietary tags (vegetarian, vegan, allergens) to let users set hard preferences
- [ ] Community ratings β surface crowd upvote counts so students can see what's popular today
- [ ] Push / SMS notifications β replace email with same-morning phone nudges
- [ ] Multi-hall support β the architecture is location-agnostic; adding other SJSU locations only requires a new location ID
- [ ] Collaborative filtering β blend the content-based (embedding) approach with userβuser similarity to surface hidden gems
- [ ] Mobile-first redesign β optimise the UI for the phone-in-hand use case students actually have standing in line
Built with β€οΈ by Sparkhill β IBM Group 29 Β· SpartHill Hacks 2026
Built With
- ai
- campus-life
- content-based-filtering
- django
- email-notifications
- embeddings
- food
- google-oauth
- hackathon
- ibm
- machine-learning
- natural-language-processing
- personalization
- python
- recommendation-system
- semantic-search
- sentence-transformers
- societal
- sustainability
- tailwindcss
- web-scraping
Log in or sign up for Devpost to join the conversation.