mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-09-30 04:13:12 +00:00
Automated weekly meal ordering from Redefine Meals — scraper, order form, payroll deductions
- Add sync-roster Lambda that auto-syncs employee roster from Slack channel membership (runs Monday 6:55am ET before menu publish) - Move FormApiKey from CloudFormation parameter/env var to Secrets Manager (meal-order-manager/form-api-key) per security conventions - Add Slack app manifest with required bot scopes - Add get_channel_members() and get_user_info() to shared Slack module - Add Lambda function ARN outputs to CloudFormation - Add log group for sync-roster Lambda (60-day retention) |
||
|---|---|---|
| .github/workflows | ||
| functions | ||
| scripts | ||
| src | ||
| .gitignore | ||
| config.json | ||
| README.md | ||
| requirements.txt | ||
| samconfig.toml.example | ||
| slack-app-manifest.yml | ||
| template.yaml | ||
meal-order-manager
Automates weekly meal ordering from Redefine Meals for Sea Haven Industries employees. Scrapes the menu, generates an order form, collects individual orders, and produces payroll deduction reports.
Architecture
Monday 8am ET Employees (Mon–Thu) Thursday 6pm ET
┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ GitHub Actions │ │ orders.seahaven │ │ EventBridge │
│ - Scrape menu │────S3 upload───▶│ ind.com │ │ - Close form │
│ - Generate form │ + DynamoDB │ (CloudFront+S3) │──POST───┐ │ - Aggregate │
│ - Slack notify │ └──────────────────┘ │ │ - Slack summary │
└─────────────────┘ ▼ └──────────────────┘
┌──────────┐
Monday 7am ET │ API GW + │
┌──────────────────┐ │ Lambda │
│ EventBridge │ │ submit │
│ - Email payroll │ └────┬─────┘
│ deductions │ ▼
└──────────────────┘ ┌──────────┐
│ DynamoDB │
Thu 10am: Slack DM │ orders │
reminders to employees └──────────┘
who haven't ordered
Weekly Flow
| When | What | How |
|---|---|---|
| Monday 7am ET | Email previous week's payroll deductions to payroll@ |
EventBridge → Lambda → SES |
| Monday 8am ET | Scrape menu, generate form, upload to S3, post link to Slack | GitHub Actions cron |
| Mon–Thu | Employees visit orders.seahavenind.com and submit orders |
S3 static form → API Gateway → Lambda → DynamoDB |
| Thursday 10am ET | DM employees who haven't ordered yet | EventBridge → Lambda → Slack DM |
| Thursday 6pm ET | Close form, aggregate orders, post Redefine order summary to Slack | EventBridge → Lambda chain |
AWS Resources
Stack name: meal-order-manager (us-east-1)
- S3 —
meal-order-manager-form-*(static form hosting),meal-order-manager-reports-*(CSV reports) - CloudFront — HTTPS distribution with custom domain
orders.seahavenind.com - DynamoDB —
meal-order-manager-orders(orders, menu, roster, config) - API Gateway — HttpApi for order submission
- Lambda — 5 functions: submit-order, close-form, aggregate-orders, slack-notifier, email-report
- EventBridge — scheduled rules (dual EST/EDT) for close, reminders, payroll email
- Secrets Manager — Slack bot token, form API key
- SES — payroll deduction emails
Setup
Local development
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
playwright install chromium
Deploy to AWS
cp samconfig.toml.example samconfig.toml
# Edit samconfig.toml with your certificate ARN, API key, etc.
sam build
sam deploy
Post-deploy
- Create the Slack bot token secret:
aws secretsmanager create-secret --name meal-order-manager/slack-bot-token --secret-string "xoxb-..." - Create the form API key secret:
aws secretsmanager create-secret --name meal-order-manager/form-api-key --secret-string "$(openssl rand -hex 32)" - Update the Slack channel SSM parameter:
aws ssm put-parameter --name /meal-order-manager/slack-channel-id --value "C0XXXXXXX" --overwrite - Verify SES sender identity for
adam@seahavenind.com - Set up DNS: CNAME
orders.seahavenind.com→ CloudFront distribution domain - Populate employee roster: edit
config.jsonand runpython3 scripts/seed_roster.py
Local Workflow (no AWS)
The scraper, form generator, Flask server, and aggregator still work locally:
python3 src/scraper/scrape_menu.py # scrape menu
python3 src/server/generate_form.py # generate form (local mode)
python3 src/server/app.py # serve on localhost:5050
python3 src/aggregator/aggregate.py # generate CSV reports
Configuration
config.json (local dev):
menu_url— Redefine Meals menu URLorder_deadline— displayed on the formroster— employee list (name, email, slack_user_id)output_dir/orders_dir— local output paths
Project Structure
meal-order-manager/
├── .github/workflows/
│ ├── weekly-menu.yml # Monday cron: scrape + publish + notify
│ ├── ci.yml # PR checks
│ └── deploy.yml # Push to main: sam deploy
├── src/
│ ├── scraper/ # Playwright menu scraper
│ ├── server/ # Form generator + local Flask server
│ ├── aggregator/ # Order aggregation + CSV reports
│ └── shared/python/shared/ # Lambda layer (db, secrets, slack helpers)
├── functions/ # Lambda handlers
│ ├── submit_order/
│ ├── close_form/
│ ├── aggregate_orders/
│ ├── slack_notifier/
│ └── email_report/
├── scripts/ # CI/CD helper scripts
│ ├── upload_menu.py
│ ├── notify_slack.py
│ └── seed_roster.py
├── template.yaml # SAM template
├── samconfig.toml.example
└── config.json