""" Generates a self-contained HTML order form from the latest scraped menu. The form shows this week's meals with quantity selectors, a running total, and submits orders to the backend (local Flask or cloud API Gateway). Usage: python3 generate_form.py # local mode python3 generate_form.py --api-url URL --api-key KEY # cloud mode """ import argparse import json import sys from datetime import datetime from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] OUTPUT_DIR = PROJECT_ROOT / "output" CONFIG_PATH = PROJECT_ROOT / "config.json" def load_config(): with open(CONFIG_PATH) as f: return json.load(f) def latest_menu() -> dict: files = sorted(OUTPUT_DIR.glob("menu-*.json"), reverse=True) if not files: print("Error: No menu JSON found. Run scrape_menu.py first.", file=sys.stderr) sys.exit(1) with open(files[0]) as f: return json.load(f) def generate_form( menu: dict, config: dict, api_url: str = "", api_key: str = "", bulk_discount: float = 0, company_subsidy: float = 0, google_client_id: str = "", ) -> str: meals_json = json.dumps(menu["meals"]).replace(" 0 or company_subsidy > 0 use_google_auth = bool(google_client_id) google_client_id_json = json.dumps(google_client_id).replace("
""" else: auth_section_html = """
""" if use_google_auth: google_auth_js = """ function waitForGoogleAuth() { if (typeof google !== 'undefined' && google.accounts && google.accounts.id) { initGoogleAuth(); } else { setTimeout(waitForGoogleAuth, 50); } } function initGoogleAuth() { google.accounts.id.initialize({ client_id: GOOGLE_CLIENT_ID, callback: handleCredentialResponse, hosted_domain: 'seahavenind.com', auto_select: true, }); google.accounts.id.renderButton( document.getElementById('g-signin-btn'), { theme: 'outline', size: 'large', text: 'signin_with', width: 300 } ); } function handleCredentialResponse(response) { googleCredential = response.credential; const b64 = response.credential.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'); const payload = JSON.parse(atob(b64)); googleUser = { name: payload.name, email: payload.email }; document.getElementById('auth-overlay').style.display = 'none'; document.getElementById('app').style.display = 'block'; var footer = document.getElementById('sticky-footer'); if (footer) footer.style.display = 'block'; document.getElementById('user-name').textContent = payload.name; document.getElementById('user-email').textContent = payload.email; if (payload.picture) { const avatar = document.getElementById('user-avatar'); avatar.src = payload.picture; avatar.style.display = 'block'; } updateTotal(); checkDuplicateOrder(); } function signOut() { googleCredential = null; googleUser = null; google.accounts.id.disableAutoSelect(); document.getElementById('auth-overlay').style.display = 'flex'; document.getElementById('app').style.display = 'none'; var footer = document.getElementById('sticky-footer'); if (footer) footer.style.display = 'none'; } """ else: google_auth_js = "" if use_google_auth: submit_order_js = """ async function submitOrder() { if (formClosed) { alert('Orders are closed.'); return; } if (!googleCredential || !googleUser) { alert('Please sign in with Google first.'); return; } const items = Object.entries(quantities).map(([i, qty]) => ({ name: MEALS[i].name, retail_price: MEALS[i].price, quantity: qty, })); const btn = document.getElementById('submit-btn'); btn.disabled = true; btn.textContent = 'Submitting...'; try { const headers = { 'Content-Type': 'application/json' }; if (API_KEY) headers['x-api-key'] = API_KEY; const res = await fetch(SUBMIT_URL, { method: 'POST', headers, body: JSON.stringify({ google_id_token: googleCredential, items }), }); const data = await res.json(); if (res.ok) { for (const el of document.getElementById('app').children) { if (el.id !== 'success') el.style.display = 'none'; } document.getElementById('success').style.display = 'block'; document.getElementById('success-detail').textContent = `${googleUser.name} — $${(data.total || 0).toFixed(2)} total. You're all set!`; document.querySelector('.sticky-footer').style.display = 'none'; try { localStorage.setItem('lastOrderWeek', WEEK); } catch (e) {} } else { alert(data.error || 'Something went wrong.'); btn.disabled = false; btn.textContent = 'Submit Order'; } } catch (e) { alert('Failed to submit. Check your connection and try again.'); btn.disabled = false; btn.textContent = 'Submit Order'; } } """ else: submit_order_js = """ async function submitOrder() { if (formClosed) { alert('Orders are closed.'); return; } const name = document.getElementById('emp-name').value.trim(); const email = document.getElementById('emp-email').value.trim(); if (!name) { alert('Please enter your name.'); return; } if (!email) { alert('Please enter your email.'); return; } const items = Object.entries(quantities).map(([i, qty]) => ({ name: MEALS[i].name, retail_price: MEALS[i].price, quantity: qty, })); const btn = document.getElementById('submit-btn'); btn.disabled = true; btn.textContent = 'Submitting...'; try { const headers = { 'Content-Type': 'application/json' }; if (API_KEY) headers['x-api-key'] = API_KEY; const res = await fetch(SUBMIT_URL, { method: 'POST', headers, body: JSON.stringify({ employee_name: name, employee_email: email, items }), }); const data = await res.json(); if (res.ok) { for (const el of document.getElementById('app').children) { if (el.id !== 'success') el.style.display = 'none'; } document.getElementById('success').style.display = 'block'; document.getElementById('success-detail').textContent = `${name} — $${(data.total || 0).toFixed(2)} total. You're all set!`; document.querySelector('.sticky-footer').style.display = 'none'; try { localStorage.setItem('lastOrderWeek', WEEK); } catch (e) {} } else { alert(data.error || 'Something went wrong.'); btn.disabled = false; btn.textContent = 'Submit Order'; } } catch (e) { alert('Failed to submit. Check your connection and try again.'); btn.disabled = false; btn.textContent = 'Submit Order'; } } """ return f""" Sea Haven — Meal Order ({week}) { '' if use_google_auth else "" }

Orders Are Closed

Orders open again Monday at 8:00 AM ET

until orders open
{ '

Sea Haven Meal Order

Sign in with your company Google account to place your order.

' if use_google_auth else "" }

Sea Haven Meal Order

Week of {week} · Menu scraped {scraped_at[:10]}

Order deadline: {deadline}
{ '
Prices reflect employee cost after ' + (f"{bulk_discount:g}% bulk discount" if bulk_discount > 0 else "") + (" + " if bulk_discount > 0 and company_subsidy > 0 else "") + (f"{company_subsidy:g}% company subsidy" if company_subsidy > 0 else "") + "
" if has_discount else "" } {auth_section_html}
""" def main(): parser = argparse.ArgumentParser(description="Generate meal order form HTML") parser.add_argument( "--api-url", default="", help="API Gateway base URL (cloud mode)" ) parser.add_argument( "--api-key", default="", help="API key for order submission (cloud mode)" ) parser.add_argument( "--bulk-discount", type=float, default=None, help="Bulk discount percentage (e.g., 10 for 10%% off)", ) parser.add_argument( "--company-subsidy", type=float, default=None, help="Company subsidy percentage (e.g., 50 for 50%% off after bulk discount)", ) parser.add_argument( "--google-client-id", default=None, help="Google OAuth Client ID for Sign-In authentication", ) args = parser.parse_args() config = load_config() menu = latest_menu() bulk_discount = ( args.bulk_discount if args.bulk_discount is not None else config.get("bulk_discount_percent", 0) ) company_subsidy = ( args.company_subsidy if args.company_subsidy is not None else config.get("company_subsidy_percent", 0) ) google_client_id = args.google_client_id or config.get("google_client_id", "") if not google_client_id: try: import boto3 ssm = boto3.client("ssm") resp = ssm.get_parameter(Name="/meal-order-manager/google-client-id") google_client_id = resp["Parameter"]["Value"] except Exception: pass html = generate_form( menu, config, api_url=args.api_url, api_key=args.api_key, bulk_discount=bulk_discount, company_subsidy=company_subsidy, google_client_id=google_client_id, ) week = datetime.now().strftime("%Y-W%U") output_file = OUTPUT_DIR / f"order-form-{week}.html" with open(output_file, "w") as f: f.write(html) mode = "cloud" if args.api_url else "local" print(f"Generated order form ({mode} mode): {output_file}") print(f" {menu['meal_count']} meals from menu scraped {menu['scraped_at'][:10]}") if mode == "local": print(" Start the server with: python3 src/server/app.py") else: print(f" API endpoint: {args.api_url}") if __name__ == "__main__": main()