Skip to content

astoltz/school-lunch-menu

Repository files navigation

School Lunch Menu Calendar Generator

.NET Build Apple Build CodeQL

A multi-platform application that generates allergen-aware, printable lunch calendars for any school in the LINQ Connect system. It is available on Windows (WPF/.NET), macOS (SwiftUI), and iPhone/iPad (SwiftUI). It fetches menu data from the LINQ Connect API, analyzes each entree for allergen safety, and produces a color-coded HTML calendar designed for easy scanning and printing. Defaults are pre-configured for Lakeville Area Schools.

Features

  • Live API and offline HAR file support -- fetch menus directly from the LINQ Connect API, or load a previously captured HAR file for offline use.
  • 17 allergen filters -- select any combination of allergens; entrees containing them are flagged with strikethrough text.
  • Braces-friendly filter -- optional 🦷 marker next to hard, crunchy, chewy, or sticky items (popcorn, hard pretzels, raw apples, sticky candy, beef jerky, etc.) so families with kids in orthodontia can scan the calendar at a glance. Items aren't removed β€” they're flagged so the family can still decide.
  • ADHD-friendly color-coded calendar -- per-plan color-coded badges, "From Home" badges, gray no-school days, favorite highlighting. High-contrast colors chosen for quick visual scanning.
  • 21 visual themes -- seasonal, fun, and basic themes with auto-suggestion by month. Themes can be hidden via settings.
  • Three calendar layout modes -- List (classic stacked badges), Icons Left, or Icons Right. Grid modes show per-plan icon buttons alongside food items in a CSS Grid layout with tinted row backgrounds. Per-plan emoji icons, short labels (click-to-edit), and display order are user-customizable. Common plan names auto-collapse to cleaner labels and icons such as Breakfast, Lunch, and Big Cat.
  • Keyboard-friendly Windows workflow -- labeled controls, access keys, quick Breakfast/Lunch session chips, compact month/year navigation, and shortcuts like Alt+B (browser), Alt+P (preview), Alt+Left, and Alt+Right.
  • Customizable holiday icons -- configure emoji and optional messages for no-school days by keyword. Defaults pre-populated from common US holidays.
  • Cross out past days -- optional faded overlay with a subtle X on past days, like crossing off a wall calendar. Days are marked past only after 3 PM (end of school day).
  • Rotating day labels -- configurable corner triangles for alternating schedules (Red/White Day, Day A/B). Fetch from CMS now pulls both the currently selected month and the following month so cross-month rotations are visible without a re-fetch. Fetched assignments preserve exact dates and treat gaps inside the fetched window as intentional blanks.
  • Share link on calendar -- optional footer with two QR codes: one linking to the school's LINQ Connect menu page and one to the project GitHub page, both embedded as self-contained base64 PNGs.
  • Source link -- "View Source" button in the status bar opens the LINQ Connect public menu page for your school in the default browser.
  • Preview zoom -- adjustable +/- zoom in the status bar (5% increments, default 100%). Zoom applies only to the in-app preview; the saved HTML file and browser view are unaffected.
  • Preview toggle -- hide the preview pane entirely when you want a narrow, sidebar-only window. Open in browser stays available without re-expanding the preview.
  • Printable landscape HTML -- self-contained HTML with @page landscape, print-color-adjust: exact, and compact print-only spacing so common month layouts stay on a single page.
  • Forced home days -- per-session configurable weekdays that always show a "From Home" badge, regardless of safe options.
  • Persisted settings -- selected allergens, forced home days, not-preferred/favorite foods, theme, layout mode, plan icons/order, and holiday overrides are saved to a JSON file and restored on next launch. Repeated entries are normalized away on load/save, and not-preferred takes precedence over favorite if stale settings contain both.
  • Settings drift sync -- when you load menu data, saved favorites and not-preferred names are reconciled against the live recipe catalog using a fuzzy match (character bigram Dice β‰₯ 0.7). Renames between deploys (PB&J Uncrustable β†’ PBJ Uncrustables) migrate automatically; unmatched names are preserved in case they appear in a future month. The status bar reports how many items were synced.
  • Dynamic school support -- enter any LINQ Connect identifier code to look up a district and its buildings. All menu plans are discovered dynamically.
  • About screen -- Help > About (Windows), App > About (macOS), or the in-app About screen (iPhone/iPad) shows version, git commit/branch, build date, .NET / OS info, and a one-click "Copy Diagnostics" button that puts a paste-ready bug report on the clipboard.

Screenshots

Windows screenshot

Getting Started

Prerequisites

Windows:

  • .NET 10 SDK (LTS through November 2028; 10.0.7 is the latest patch as of April 2026)
  • Windows 10 or later
  • If you're working from WSL, use dotnet.exe / powershell.exe for the WPF app and tests.

Apple platforms:

  • macOS 13 (Ventura) or later
  • Xcode 15+ with Swift 5.9+
  • iOS/iPadOS 17+ for device testing

Building the Windows Version

# Build
dotnet build school-lunch-menu.Windows/SchoolLunchMenu.csproj

# Run
dotnet run --project school-lunch-menu.Windows/SchoolLunchMenu.csproj

# Publish self-contained executable
dotnet publish school-lunch-menu.Windows/SchoolLunchMenu.csproj --configuration Release --self-contained -r win-x64 -p:PublishSingleFile=true

From WSL, prefer:

dotnet.exe build school-lunch-menu.Windows/SchoolLunchMenu.csproj
dotnet.exe test tests/SchoolLunchMenu.Tests/SchoolLunchMenu.Tests.csproj
dotnet.exe test tests/SchoolLunchMenu.UiTests/SchoolLunchMenu.UiTests.csproj

Building the Apple Versions

# Build the macOS app
cd school-lunch-menu.Mac
xcodebuild -project SchoolLunchMenu.xcodeproj -scheme SchoolLunchMenu -configuration Release build

# Build the iPhone/iPad app for Simulator
xcodebuild -project SchoolLunchMenu.xcodeproj -scheme SchoolLunchMenuiOS -configuration Debug -destination 'generic/platform=iOS Simulator' build

# Or open in Xcode
open school-lunch-menu.Mac/SchoolLunchMenu.xcodeproj

In Xcode, use the SchoolLunchMenu scheme for macOS and SchoolLunchMenuiOS for iPhone/iPad.

For device installs, archives, or any signed build, fill in school-lunch-menu.Mac/Configs/LocalSigning.xcconfig from school-lunch-menu.Mac/Configs/LocalSigning.xcconfig.example, then open Signing & Capabilities if you want to confirm the resolved team and bundle IDs in Xcode. See docs/APPLE-SIGNING.md for the exact workflow and non-App-Store distribution options.

Apple Archive Scripts

# macOS archive
./school-lunch-menu.Mac/scripts/archive-macos.sh

# macOS Developer ID export (after archiving)
./school-lunch-menu.Mac/scripts/export-macos-developer-id.sh

# macOS notarization + stapling (required before broad outside-the-store sharing)
NOTARY_PROFILE=school-lunch-menu-notary ./school-lunch-menu.Mac/scripts/notarize-macos.sh

# iPhone/iPad archive
./school-lunch-menu.Mac/scripts/archive-ios.sh

# iPhone/iPad Ad Hoc export (after archiving)
./school-lunch-menu.Mac/scripts/export-ios-adhoc.sh

These scripts use the local signing overrides in school-lunch-menu.Mac/Configs/LocalSigning.xcconfig if present. The macOS Developer ID export still needs notarization before Gatekeeper will accept it on other Macs.

Usage

  1. Enter an identifier code and click Look Up to find your school district.
  2. Select a building from the dropdown.
  3. Pick a month and year. On Windows you can use the compact month navigator or Alt+Left / Alt+Right.
  4. Pick a serving session. On Windows, the quick session chips make Breakfast/Lunch switching faster than reopening a dropdown.
  5. Click Fetch from API to download menu data from LINQ Connect, or click Load from HAR to load a previously saved .har file.
  6. Check the allergens you want to filter (Milk is selected by default).
  7. Optionally mark foods as not preferred or favorites.
  8. Configure forced home days (default: Thursday) per serving session.
  9. Optionally change Day Layout to "Icons Left" or "Icons Right" to see per-line icon buttons. Less-used plan icon/label/reorder controls live under the Plan Buttons & Icons expander.
  10. Optionally customize holiday icons in the Holiday Icons section.
  11. Optionally check Cross out past days to fade and X-out past dates.
  12. Optionally check Share link on calendar to add a QR code footer linking to the project page.
  13. Optionally expand Day Labels to set up rotating labels (e.g., Red Day / White Day). Add labels with colors, choose a corner position, and optionally set a start date.
  14. Click Generate Calendar to produce the HTML calendar. It's displayed in the preview pane (use +/- to zoom) and saved to a temporary directory.
  15. Click Open in Browser to launch in your default browser, or View Source in the status bar to see the LINQ Connect public menu page.
  16. Print the page in landscape orientation from the browser.

Configuration

Settings are persisted to a JSON file and restored on next launch:

  • Windows: settings.json next to the executable
  • macOS: ~/Library/Application Support/SchoolLunchMenu/settings.json
  • iPhone/iPad: app sandbox Application Support/SchoolLunchMenu/settings.json

Settings Structure

{
  "SelectedAllergenIds": ["4dce70b9-238e-ea11-bd68-f554d510c22b"],
  "ForcedHomeDaysBySession": {
    "Lunch": ["Thursday"]
  },
  "NotPreferredBySession": {
    "Lunch": ["PBJ Uncrustable"]
  },
  "FavoritesBySession": {
    "Lunch": ["Hamburger on Bun"]
  },
  "Identifier": "YVAM38",
  "BuildingId": "c895c20a-5e8e-ea11-bd68-f554d510c22b",
  "SelectedSessionName": "Lunch",
  "SelectedThemeName": "Default",
  "LayoutMode": "IconsLeft",
  "PlanLabelOverrides": {
    "Lunch - Middle School 25-26": "Lunch"
  },
  "PlanIconOverrides": {
    "Lunch - Middle School 25-26": "🍽️"
  },
  "CrossOutPastDays": true,
  "BracesFriendlyFilter": false,
  "ShowShareFooter": false,
  "DayLabelsEnabled": true,
  "DayLabelCycle": [
    { "Label": "Red", "Color": "#dc3545" },
    { "Label": "White", "Color": "#adb5bd" }
  ],
  "DayLabelAssignments": [
    { "Date": "2026-03-19", "Label": "Red", "Color": "#dc3545" }
  ],
  "DayLabelCoverageStartDate": "2026-03-01",
  "DayLabelCoverageEndDate": "2026-04-04",
  "DayLabelCorner": "TopLeft"
}

Development

Repository Structure

school-lunch-menu/
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       β”œβ”€β”€ dotnet.yml           # Windows CI/CD pipeline
β”‚       └── swift.yml            # Apple CI/CD pipeline
β”œβ”€β”€ school-lunch-menu.Windows/   # Windows WPF application (.NET 10)
β”‚   β”œβ”€β”€ App.xaml.cs              # DI container setup, Serilog configuration
β”‚   β”œβ”€β”€ MainWindow.xaml          # WPF layout with guided sidebar flow and WebView2 preview
β”‚   β”œβ”€β”€ ViewModels/              # MainViewModel (MVVM with CommunityToolkit)
β”‚   β”œβ”€β”€ Models/                  # ProcessedDay, ProcessedMonth, RecipeItem, AppSettings
β”‚   β”œβ”€β”€ Models/Api/              # LINQ Connect API response DTOs
β”‚   β”œβ”€β”€ Services/                # API client, HAR parser, menu analyzer, HTML generator
β”‚   └── Converters/              # WPF value converters
β”œβ”€β”€ school-lunch-menu.Mac/       # Apple SwiftUI application (macOS + iPhone/iPad)
β”‚   β”œβ”€β”€ SchoolLunchMenu.xcodeproj
β”‚   β”œβ”€β”€ Configs/                 # Shared signing defaults + local override example
β”‚   β”œβ”€β”€ scripts/                 # Apple archive/export helper scripts
β”‚   └── SchoolLunchMenu/
β”‚       β”œβ”€β”€ App/                 # SwiftUI app entry point
β”‚       β”œβ”€β”€ Views/               # Shared SwiftUI views (MainView, SidebarView, PreviewView)
β”‚       β”œβ”€β”€ ViewModels/          # MainViewModel shared across Apple platforms
β”‚       β”œβ”€β”€ Models/              # Swift model types matching Windows DTOs
β”‚       β”œβ”€β”€ Models/Api/          # LINQ Connect API response Codable types
β”‚       β”œβ”€β”€ Services/            # API client, HAR parser, menu analyzer, HTML generator
β”‚       └── Resources/           # Asset catalogs
β”œβ”€β”€ docs/                        # Project documentation
β”œβ”€β”€ scripts/                     # Utility scripts (for example screenshot capture)
β”œβ”€β”€ SchoolLunchMenu.slnx         # Visual Studio solution file
└── README.md

Architecture Overview

Both platforms follow the MVVM (Model-View-ViewModel) pattern:

  • Models define data structures for API responses, processed menu data, and application settings
  • ViewModels contain business logic, state management, and commands
  • Views handle UI rendering and user interaction
  • Services encapsulate external concerns (API calls, file I/O, HTML generation)

The Windows version uses:

  • WPF with XAML for UI
  • CommunityToolkit.Mvvm for MVVM infrastructure
  • Microsoft.Extensions.DependencyInjection for DI
  • Serilog for logging

The Apple versions use:

  • SwiftUI with the Observation framework
  • Native Swift Codable for JSON serialization
  • URLSession for networking
  • WebKit for HTML preview
  • Shared Swift services and view models across macOS and iPhone/iPad

Testing

The Windows side includes both unit tests and FlaUI-based UI smoke tests:

dotnet test tests/SchoolLunchMenu.Tests/SchoolLunchMenu.Tests.csproj
dotnet test tests/SchoolLunchMenu.UiTests/SchoolLunchMenu.UiTests.csproj

Tests cover:

  • MenuAnalyzer -- allergen safety, not-preferred, favorites, no-school days, multi-plan analysis, braces-friendly flagging on/off
  • CalendarHtmlGenerator -- HTML output, themes, grid mode, past days, day labels, share footer
  • SettingsService -- default settings, round-trip persistence
  • DayLabelFetchService -- CMS HTML parsing regex patterns, cal_date cross-month URL construction, full HTTP capture round-trip
  • BracesFoodAdvisor -- positive matches (popcorn, jerky, raw apples), negative matches (soft pretzel, popcorn chicken, mac & cheese), case-insensitive, empty-string handling
  • PreferenceReconciler -- exact match, fuzzy rename via Dice coefficient (β‰₯ 0.7), unmatched-name preservation, multi-session, threshold rejection of unrelated names
  • AboutDiagnostics -- diagnostic block contains expected sections and reports the live .NET runtime
  • FlaUI smoke tests -- app launch, core controls, session selector, preview toggle, braces filter checkbox discovery, and end-to-end calendar generation
  • ProcessedDay -- computed properties (IsNoSchool, AnyLineSafe, HasMenu)
  • CalendarThemes -- 21 themes, categories, auto-suggestion coverage

Total: 99 unit tests (62 β†’ 99, +37 new) plus the 3-test FlaUI smoke suite.

The Apple targets are currently verified by GitHub Actions builds for macOS and iOS Simulator. A dedicated Swift test target does not exist yet.

Automation & Handoff

  • Dashboards -- the README badges at the top link to the active GitHub Actions dashboards for .NET Build, Apple Build, and CodeQL.
  • Dependency updates -- .github/dependabot.yml enables weekly GitHub Actions and NuGet update scans.
  • Agent guidance -- CLAUDE.md and CODEX.md describe repo-specific tooling expectations for Windows-from-WSL and Mac verification.
  • Resume docs -- docs/AI-HANDOFF.md captures the current branch state, validated work, remaining Mac-side checks, and the fastest commands to continue.
  • Mac continuation prompt -- docs/mac-verify-prompt.md is intended to be pasted directly into another Claude/Codex instance running on a Mac.
  • Plugins / skills -- there are no repo-local plugin manifests or Codex skill bundles checked into this repository. The supported automation surface is the normal CLI stack: gh, dotnet.exe, powershell.exe, xcodebuild, and Xcode.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

About

Dual-platform (WPF + SwiftUI) allergen-aware school lunch calendar generator for LINQ Connect menus

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors