Compare commits
No commits in common. "27280f50397a6416478ad36d577626dbe9f37004" and "cf6dbce3bb0a52b26f50ad0b3283ed88422ccf52" have entirely different histories.
27280f5039
...
cf6dbce3bb
@ -1 +1 @@
|
|||||||
v20260103-08-reports-stats-endpoint-fix
|
v20260103-07-reports-advanced-reporting-foundation
|
||||||
|
|||||||
@ -229,6 +229,117 @@ def api_reports_columns():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@main_bp.route("/api/reports/<int:report_id>/stats", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def api_reports_stats(report_id: int):
|
||||||
|
"""Return lightweight KPI + chart datasets for a report.
|
||||||
|
|
||||||
|
Data is derived from report_object_snapshots, which is generated by
|
||||||
|
POST /api/reports/<id>/generate.
|
||||||
|
"""
|
||||||
|
err = _require_reporting_role()
|
||||||
|
if err is not None:
|
||||||
|
return err
|
||||||
|
|
||||||
|
ReportDefinition.query.get_or_404(report_id)
|
||||||
|
|
||||||
|
# KPI counts
|
||||||
|
# We treat missed runs as their own bucket, regardless of status string.
|
||||||
|
row = db.session.execute(
|
||||||
|
text(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
COUNT(*)::INTEGER AS total_runs,
|
||||||
|
SUM(CASE WHEN missed = TRUE THEN 1 ELSE 0 END)::INTEGER AS missed_runs,
|
||||||
|
SUM(CASE WHEN missed = FALSE AND COALESCE(status,'') ILIKE 'success%' THEN 1 ELSE 0 END)::INTEGER AS success_runs,
|
||||||
|
SUM(CASE WHEN missed = FALSE AND COALESCE(status,'') ILIKE 'warning%' THEN 1 ELSE 0 END)::INTEGER AS warning_runs,
|
||||||
|
SUM(CASE WHEN missed = FALSE AND COALESCE(status,'') ILIKE 'fail%' THEN 1 ELSE 0 END)::INTEGER AS failed_runs,
|
||||||
|
SUM(CASE WHEN override_applied = TRUE THEN 1 ELSE 0 END)::INTEGER AS override_runs
|
||||||
|
FROM report_object_snapshots
|
||||||
|
WHERE report_id = :rid
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
{"rid": report_id},
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
total_runs = int(row.total_runs or 0) if row else 0
|
||||||
|
success_runs = int(row.success_runs or 0) if row else 0
|
||||||
|
warning_runs = int(row.warning_runs or 0) if row else 0
|
||||||
|
failed_runs = int(row.failed_runs or 0) if row else 0
|
||||||
|
missed_runs = int(row.missed_runs or 0) if row else 0
|
||||||
|
override_runs = int(row.override_runs or 0) if row else 0
|
||||||
|
|
||||||
|
success_rate = 0.0
|
||||||
|
if total_runs > 0:
|
||||||
|
# Consider overrides as success for success_rate.
|
||||||
|
success_rate = ((success_runs + override_runs) / float(total_runs)) * 100.0
|
||||||
|
|
||||||
|
# Trend datasets (per day)
|
||||||
|
trend_rows = db.session.execute(
|
||||||
|
text(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
DATE_TRUNC('day', run_at) AS day,
|
||||||
|
COUNT(*)::INTEGER AS total,
|
||||||
|
SUM(CASE WHEN missed = TRUE THEN 1 ELSE 0 END)::INTEGER AS missed,
|
||||||
|
SUM(CASE WHEN missed = FALSE AND COALESCE(status,'') ILIKE 'success%' THEN 1 ELSE 0 END)::INTEGER AS success,
|
||||||
|
SUM(CASE WHEN missed = FALSE AND COALESCE(status,'') ILIKE 'warning%' THEN 1 ELSE 0 END)::INTEGER AS warning,
|
||||||
|
SUM(CASE WHEN missed = FALSE AND COALESCE(status,'') ILIKE 'fail%' THEN 1 ELSE 0 END)::INTEGER AS failed
|
||||||
|
FROM report_object_snapshots
|
||||||
|
WHERE report_id = :rid
|
||||||
|
AND run_at IS NOT NULL
|
||||||
|
GROUP BY DATE_TRUNC('day', run_at)
|
||||||
|
ORDER BY DATE_TRUNC('day', run_at) ASC
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
{"rid": report_id},
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
|
trend = []
|
||||||
|
for tr in trend_rows or []:
|
||||||
|
day = tr.day
|
||||||
|
day_iso = day.date().isoformat() if hasattr(day, "date") else str(day)
|
||||||
|
total = int(tr.total or 0)
|
||||||
|
succ = int(tr.success or 0)
|
||||||
|
fail = int(tr.failed or 0)
|
||||||
|
succ_rate = 0.0
|
||||||
|
if total > 0:
|
||||||
|
succ_rate = (succ / float(total)) * 100.0
|
||||||
|
trend.append(
|
||||||
|
{
|
||||||
|
"day": day_iso,
|
||||||
|
"total": total,
|
||||||
|
"success": succ,
|
||||||
|
"warning": int(tr.warning or 0),
|
||||||
|
"failed": fail,
|
||||||
|
"missed": int(tr.missed or 0),
|
||||||
|
"success_rate": succ_rate,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"kpis": {
|
||||||
|
"total_runs": total_runs,
|
||||||
|
"success_runs": success_runs,
|
||||||
|
"warning_runs": warning_runs,
|
||||||
|
"failed_runs": failed_runs,
|
||||||
|
"missed_runs": missed_runs,
|
||||||
|
"override_runs": override_runs,
|
||||||
|
"success_rate": float(success_rate),
|
||||||
|
},
|
||||||
|
"charts": {
|
||||||
|
"status_distribution": {
|
||||||
|
"success": success_runs + override_runs,
|
||||||
|
"warning": warning_runs,
|
||||||
|
"failed": failed_runs,
|
||||||
|
"missed": missed_runs,
|
||||||
|
},
|
||||||
|
"trend": trend,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route("/api/reports/<int:report_id>", methods=["DELETE"])
|
@main_bp.route("/api/reports/<int:report_id>", methods=["DELETE"])
|
||||||
@login_required
|
@login_required
|
||||||
def api_reports_delete(report_id: int):
|
def api_reports_delete(report_id: int):
|
||||||
|
|||||||
@ -62,14 +62,6 @@
|
|||||||
### Fixed
|
### Fixed
|
||||||
- Ensured report deletion flow remains compatible with extended report definition handling.
|
- Ensured report deletion flow remains compatible with extended report definition handling.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## v20260103-08-reports-stats-endpoint-fix
|
|
||||||
|
|
||||||
- Fixed application startup crash caused by duplicate registration of the `api_reports_stats` endpoint.
|
|
||||||
- Removed the redundant `/api/reports/<report_id>/stats` route definition to ensure the endpoint is registered only once.
|
|
||||||
- Restored proper Gunicorn boot sequence by resolving Flask endpoint name collision.
|
|
||||||
|
|
||||||
================================================================================================================================================
|
================================================================================================================================================
|
||||||
|
|
||||||
## v0.1.15
|
## v0.1.15
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user