mirror of
https://github.com/Sea-Haven-Industries/apm-wo-analysis.git
synced 2026-09-30 12:23:15 +00:00
Two issues found loading the deployed dashboard: 1. Panels used type "bar-chart" (hyphenated); Grafana's core panel is "barchart" — hence "plugin bar-chart required". Fixed both panels. 2. ALL panels showed "no data" because Athena failed with HIVE_BAD_DATA: the classifier wrote summary.json/details.json INTO analytics/dt=*/ — the same prefix the Glue table scans — so Athena tried to read the JSON as Parquet and every query failed. Move the metadata to a separate meta/dt=*/ prefix: classifier writes there (grant_read_write meta/*), the Slack Lambdas read there (read_meta_json, grant_read meta/*), and analytics/ holds only Parquet. Verified: the category GROUP BY query now succeeds against Athena.
59 lines
2.3 KiB
Python
59 lines
2.3 KiB
Python
"""Slack post Lambda: daily summary + batched 3rd-escalation alert.
|
|
|
|
Async-invoked by the classifier with ``{"dt": "YYYY-MM-DD"}`` once the day's
|
|
snapshot is written. Reads today's (and yesterday's, for deltas) ``summary.json``
|
|
from ``analytics/``, posts the daily summary to the WO channel via the reused bot
|
|
token (Secrets Manager), and — only when the 3rd-escalation count > 0 — posts the
|
|
standalone batched alert (reading ``details.json`` for the 3rd-escalation rows).
|
|
|
|
Runtime: Python 3.12, ARM64. No App Home — two push surfaces only (see CLAUDE.md).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
import blockkit
|
|
import slackio
|
|
|
|
|
|
def _yesterday(dt: str) -> str:
|
|
return (datetime.strptime(dt, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
|
|
|
|
|
|
def handler(event, context):
|
|
"""Post the daily summary and (conditionally) the 3rd-escalation alert."""
|
|
dt = (event or {}).get("dt") or datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
|
|
today = slackio.read_meta_json(dt, "summary.json")
|
|
if today is None:
|
|
print(f"No summary.json for dt={dt}; nothing to post.")
|
|
return {"posted": False, "reason": "no summary", "dt": dt}
|
|
yesterday = slackio.read_meta_json(_yesterday(dt), "summary.json")
|
|
|
|
client = slackio.web_client()
|
|
channel = slackio.channel_id()
|
|
dashboard_url = slackio.get_dashboard_url()
|
|
|
|
summary_blocks = blockkit.build_daily_summary(today, yesterday, dashboard_url)
|
|
client.chat_postMessage(
|
|
channel=channel, blocks=summary_blocks, text=f"APM Work Orders — {dt}"
|
|
)
|
|
posted = {"summary": True, "alert": False}
|
|
|
|
# Standalone batched alert — only when there are 3rd escalations. Pull the
|
|
# rows from details.json so the alert can name the WOs.
|
|
if today.get("third_escalation_count", 0) > 0:
|
|
details = slackio.read_meta_json(dt, "details.json") or []
|
|
thirds = [d for d in details if d.get("category") == "3rd Escalation"]
|
|
alert_blocks = blockkit.build_escalation_alert(thirds)
|
|
if alert_blocks:
|
|
client.chat_postMessage(
|
|
channel=channel,
|
|
blocks=alert_blocks,
|
|
text=f"🚨 {len(thirds)} 3rd-escalation work orders",
|
|
)
|
|
posted["alert"] = True
|
|
|
|
print(f"Posted dt={dt}: {posted}")
|
|
return {"posted": True, "dt": dt, **posted}
|