meal-order-manager/functions/slack_notifier/handler.py
Adam Moussa 440117c9a1
Fix ruff lint and format violations, update README (#5)
Apply ruff check --fix and ruff format across all Python files to
pass CI pipeline. Remove unused imports (os, sys), fix f-strings
without placeholders. Update README to reflect sync-roster Lambda,
corrected shared layer path, and current project structure.
2026-05-12 19:31:27 -04:00

144 lines
4.2 KiB
Python

import json
import os
from decimal import Decimal
from shared.db import current_week, get_orders, get_roster, get_summary
from shared.slack import post_channel_message, send_dm
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, Decimal):
return float(o)
return super().default(o)
def lambda_handler(event, context):
event_type = event.get("event", "")
if event_type == "menu_published":
return handle_menu_published(event)
elif event_type == "orders_aggregated":
return handle_orders_aggregated(event)
elif event_type == "reminder":
return handle_reminder(event)
return {"error": f"Unknown event type: {event_type}"}
def handle_menu_published(event):
form_url = os.environ.get("FORM_URL", "")
week = event.get("week", current_week())
meal_count = event.get("meal_count", "")
text = "This week's meal order is open! Deadline: Thursday 6pm."
blocks = [
{
"type": "header",
"text": {"type": "plain_text", "text": "Meal Order Open"},
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": (
f"*<{form_url}|Place your order>*\n\n"
f"*Deadline:* Thursday 6pm\n"
f"*Menu:* {meal_count} meals available"
),
},
},
]
post_channel_message(text, blocks)
return {"status": "notified", "event": "menu_published", "week": week}
def handle_orders_aggregated(event):
week = event.get("week", current_week())
summary = get_summary(week)
if not summary:
return {"status": "no_summary", "week": week}
total_employees = int(summary.get("total_employees", 0))
total_meals = int(summary.get("total_meals", 0))
grand_total = float(summary.get("grand_total", 0))
meal_lines = []
for m in summary.get("meals", []):
meal_lines.append(f"{m['meal']}: *{int(m['quantity'])}*")
meal_list = "\n".join(meal_lines)
text = f"Meal orders closed for {week}. {total_employees} employees, {total_meals} meals, ${grand_total:.2f} total."
blocks = [
{
"type": "header",
"text": {"type": "plain_text", "text": f"Meal Orders Closed — {week}"},
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": (
f"*{total_employees}* employees ordered\n"
f"*{total_meals}* total meals\n"
f"*${grand_total:.2f}* grand total"
),
},
},
{"type": "divider"},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Order for Redefine Meals:*\n{meal_list}",
},
},
]
post_channel_message(text, blocks)
return {"status": "notified", "event": "orders_aggregated", "week": week}
def handle_reminder(event):
week = event.get("week", current_week())
form_url = os.environ.get("FORM_URL", "")
roster = get_roster()
if not roster:
return {"status": "no_roster"}
orders = get_orders(week)
ordered_emails = {o["employee_email"].lower() for o in orders}
missing = [emp for emp in roster if emp["email"].lower() not in ordered_emails]
dm_count = 0
for emp in missing:
slack_id = emp.get("slack_user_id")
if not slack_id:
continue
send_dm(
slack_id,
f"Meal orders close at 6pm today. Place your order: {form_url}",
blocks=[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": (
f"Hey {emp['name'].split()[0]}! Meal orders close at *6pm today*.\n\n"
f"*<{form_url}|Place your order>*"
),
},
}
],
)
dm_count += 1
return {
"status": "reminders_sent",
"week": week,
"dm_count": dm_count,
"missing_count": len(missing),
}