readinness

This commit is contained in:
JiriUhlir
2026-06-08 07:49:06 +02:00
parent 382825085f
commit 2f5b5bcb89
4 changed files with 246 additions and 1 deletions
+147
View File
@@ -0,0 +1,147 @@
import json
import re
from typing import Any
from app.db.database import get_connection
from app.db.migrations import run_migrations
PREFLIGHT_SCRIPT = "preflight-check.sh"
def _job_matches_script(row: dict[str, Any]) -> bool:
if row.get("target_id") == PREFLIGHT_SCRIPT:
return True
try:
payload = json.loads(row.get("payload_json") or "{}")
except (TypeError, ValueError):
return False
return payload.get("script_name") == PREFLIGHT_SCRIPT
def get_latest_preflight_job():
run_migrations()
con = get_connection()
rows = con.execute(
"""
SELECT *
FROM jobs
WHERE type = 'run_script'
AND (
target_id = ?
OR payload_json LIKE ?
)
ORDER BY id DESC
LIMIT 20
""",
(PREFLIGHT_SCRIPT, f'%"{PREFLIGHT_SCRIPT}"%'),
).fetchall()
con.close()
for row in rows:
job = dict(row)
if _job_matches_script(job):
return job
return None
def get_job_log_output(job_id: int) -> str:
run_migrations()
con = get_connection()
rows = con.execute(
"""
SELECT stream, message
FROM job_logs
WHERE job_id = ?
ORDER BY id
""",
(job_id,),
).fetchall()
con.close()
return "\n".join(f"[{row['stream'] or 'log'}] {row['message'] or ''}" for row in rows)
def _count_token(text: str, token: str) -> int:
return len(re.findall(rf"(?<![A-Z0-9_]){re.escape(token)}(?![A-Z0-9_])", text.upper()))
def _result_data(job: dict[str, Any]) -> dict[str, Any]:
try:
value = json.loads(job.get("result_json") or "{}")
except (TypeError, ValueError):
return {}
return value if isinstance(value, dict) else {}
def _first_int(data: dict[str, Any], keys: tuple[str, ...]) -> int | None:
for key in keys:
value = data.get(key)
if value is None:
continue
try:
return int(value)
except (TypeError, ValueError):
continue
return None
def summarize_readiness(job: dict[str, Any] | None) -> dict[str, Any]:
if not job:
return {
"ready": False,
"ok_count": 0,
"warn_count": 0,
"fail_count": 0,
"raw_output": "",
"job": None,
}
raw_output = get_job_log_output(int(job["id"]))
result_data = _result_data(job)
combined_text = "\n".join(
value
for value in (
raw_output,
job.get("result_json") or "",
job.get("error_text") or "",
)
if value
)
ok_count = _first_int(result_data, ("ok", "ok_count", "okCount"))
warn_count = _first_int(result_data, ("warn", "warn_count", "warnCount", "warnings"))
fail_count = _first_int(result_data, ("fail", "fail_count", "failCount", "failed", "errors"))
if ok_count is None:
ok_count = _count_token(combined_text, "OK")
if warn_count is None:
warn_count = _count_token(combined_text, "WARN")
if fail_count is None:
fail_count = _count_token(combined_text, "FAIL")
readiness_value = str(
result_data.get("readiness")
or result_data.get("ready")
or result_data.get("status")
or ""
).strip().upper()
if readiness_value in {"READY", "TRUE", "OK", "SUCCESS"}:
ready = fail_count == 0 and (job.get("status") or "").lower() == "success"
elif readiness_value in {"NOT READY", "FALSE", "FAIL", "FAILED", "ERROR"}:
ready = False
else:
ready = fail_count == 0 and (job.get("status") or "").lower() == "success"
return {
"ready": ready,
"ok_count": ok_count,
"warn_count": warn_count,
"fail_count": fail_count,
"raw_output": raw_output,
"job": job,
}