semantics
This commit is contained in:
@@ -3,14 +3,15 @@
|
||||
# @SEMANTICS: health, aggregation, dashboards
|
||||
# @PURPOSE: Business logic for aggregating dashboard health status from validation records.
|
||||
# @LAYER: Domain/Service
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.models.llm.ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.core.superset_client.SupersetClient]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.core.task_manager.cleanup.TaskCleanupService]
|
||||
# @RELATION: [DEPENDS_ON] ->[ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[SupersetClient]
|
||||
# @RELATION: [DEPENDS_ON] ->[TaskCleanupService]
|
||||
# @RELATION: [DEPENDS_ON] ->[TaskManager]
|
||||
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
from typing import List, Dict, Any, Optional, Tuple, cast
|
||||
import time
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, desc
|
||||
from sqlalchemy import func
|
||||
import os
|
||||
from ..models.llm import ValidationRecord
|
||||
from ..schemas.health import DashboardHealthItem, HealthSummaryResponse
|
||||
@@ -20,6 +21,10 @@ from ..core.task_manager.cleanup import TaskCleanupService
|
||||
from ..core.task_manager import TaskManager
|
||||
|
||||
|
||||
def _empty_dashboard_meta() -> Dict[str, Optional[str]]:
|
||||
return cast(Dict[str, Optional[str]], {"slug": None, "title": None})
|
||||
|
||||
|
||||
# [DEF:HealthService:Class]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Aggregate latest dashboard validation state and manage persisted health report lifecycle.
|
||||
@@ -27,12 +32,12 @@ from ..core.task_manager import TaskManager
|
||||
# @POST: Exposes health summary aggregation and validation report deletion operations.
|
||||
# @SIDE_EFFECT: Maintains in-memory dashboard metadata caches and may coordinate cleanup through collaborators.
|
||||
# @DATA_CONTRACT: Input[Session, Optional[Any]] -> Output[HealthSummaryResponse|bool]
|
||||
# @RELATION: [DEPENDS_ON] ->[sqlalchemy.orm.Session]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.models.llm.ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.schemas.health.DashboardHealthItem]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.schemas.health.HealthSummaryResponse]
|
||||
# @RELATION: [CALLS] ->[backend.src.core.superset_client.SupersetClient]
|
||||
# @RELATION: [CALLS] ->[backend.src.core.task_manager.cleanup.TaskCleanupService]
|
||||
# @RELATION: [DEPENDS_ON] ->[ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[DashboardHealthItem]
|
||||
# @RELATION: [DEPENDS_ON] ->[HealthSummaryResponse]
|
||||
# @RELATION: [DEPENDS_ON] ->[SupersetClient]
|
||||
# @RELATION: [DEPENDS_ON] ->[TaskCleanupService]
|
||||
# @RELATION: [DEPENDS_ON] ->[TaskManager]
|
||||
class HealthService:
|
||||
_dashboard_summary_cache: Dict[
|
||||
str, Tuple[float, Dict[str, Dict[str, Optional[str]]]]
|
||||
@@ -50,8 +55,7 @@ class HealthService:
|
||||
# @POST: Service is ready to aggregate summaries and delete health reports.
|
||||
# @SIDE_EFFECT: Initializes per-instance dashboard metadata cache.
|
||||
# @DATA_CONTRACT: Input[db: Session, config_manager: Optional[Any]] -> Output[HealthService]
|
||||
# @RELATION: [BINDS_TO] ->[backend.src.services.health_service.HealthService]
|
||||
# @RELATION: [DEPENDS_ON] ->[sqlalchemy.orm.Session]
|
||||
# @RELATION: [BINDS_TO] ->[HealthService]
|
||||
def __init__(self, db: Session, config_manager=None):
|
||||
self.db = db
|
||||
self.config_manager = config_manager
|
||||
@@ -66,10 +70,9 @@ class HealthService:
|
||||
# @POST: Numeric dashboard ids for known environments are cached when discoverable.
|
||||
# @SIDE_EFFECT: May call Superset dashboard list API once per referenced environment.
|
||||
# @DATA_CONTRACT: Input[records: List[ValidationRecord]] -> Output[None]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.models.llm.ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.core.superset_client.SupersetClient]
|
||||
# @RELATION: [CALLS] ->[config_manager.get_environments]
|
||||
# @RELATION: [CALLS] ->[backend.src.core.superset_client.SupersetClient.get_dashboards_summary]
|
||||
# @RELATION: [DEPENDS_ON] ->[ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[ConfigManager]
|
||||
# @RELATION: [DEPENDS_ON] ->[SupersetClient]
|
||||
def _prime_dashboard_meta_cache(self, records: List[ValidationRecord]) -> None:
|
||||
if not self.config_manager or not records:
|
||||
return
|
||||
@@ -98,23 +101,26 @@ class HealthService:
|
||||
env = environments.get(environment_id)
|
||||
if not env:
|
||||
for dashboard_id in dashboard_ids:
|
||||
self._dashboard_meta_cache[(environment_id, dashboard_id)] = {
|
||||
"slug": None,
|
||||
"title": None,
|
||||
}
|
||||
self._dashboard_meta_cache[(environment_id, dashboard_id)] = (
|
||||
_empty_dashboard_meta()
|
||||
)
|
||||
continue
|
||||
|
||||
try:
|
||||
cached_meta = self.__class__._dashboard_summary_cache.get(
|
||||
environment_id
|
||||
)
|
||||
cache_is_fresh = (
|
||||
dashboard_meta_map: Dict[str, Dict[str, Optional[str]]]
|
||||
if (
|
||||
cached_meta is not None
|
||||
and (time.monotonic() - cached_meta[0])
|
||||
< self.__class__._dashboard_summary_cache_ttl_seconds
|
||||
)
|
||||
if cache_is_fresh:
|
||||
dashboard_meta_map = cached_meta[1]
|
||||
):
|
||||
cached_meta_data = cast(
|
||||
Tuple[float, Dict[str, Dict[str, Optional[str]]]],
|
||||
cached_meta,
|
||||
)
|
||||
dashboard_meta_map = cached_meta_data[1]
|
||||
else:
|
||||
dashboards = SupersetClient(env).get_dashboards_summary()
|
||||
dashboard_meta_map = {
|
||||
@@ -133,7 +139,7 @@ class HealthService:
|
||||
self._dashboard_meta_cache[(environment_id, dashboard_id)] = (
|
||||
dashboard_meta_map.get(
|
||||
dashboard_id,
|
||||
{"slug": None, "title": None},
|
||||
_empty_dashboard_meta(),
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
@@ -143,10 +149,9 @@ class HealthService:
|
||||
exc,
|
||||
)
|
||||
for dashboard_id in dashboard_ids:
|
||||
self._dashboard_meta_cache[(environment_id, dashboard_id)] = {
|
||||
"slug": None,
|
||||
"title": None,
|
||||
}
|
||||
self._dashboard_meta_cache[(environment_id, dashboard_id)] = (
|
||||
_empty_dashboard_meta()
|
||||
)
|
||||
|
||||
# [/DEF:_prime_dashboard_meta_cache:Function]
|
||||
|
||||
@@ -162,20 +167,20 @@ class HealthService:
|
||||
normalized_dashboard_id = str(dashboard_id or "").strip()
|
||||
normalized_environment_id = str(environment_id or "").strip()
|
||||
if not normalized_dashboard_id:
|
||||
return {"slug": None, "title": None}
|
||||
return _empty_dashboard_meta()
|
||||
|
||||
if not normalized_dashboard_id.isdigit():
|
||||
return {"slug": normalized_dashboard_id, "title": None}
|
||||
|
||||
if not self.config_manager or not normalized_environment_id:
|
||||
return {"slug": None, "title": None}
|
||||
return _empty_dashboard_meta()
|
||||
|
||||
cache_key = (normalized_environment_id, normalized_dashboard_id)
|
||||
cached = self._dashboard_meta_cache.get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
meta = {"slug": None, "title": None}
|
||||
meta = _empty_dashboard_meta()
|
||||
self._dashboard_meta_cache[cache_key] = meta
|
||||
return meta
|
||||
|
||||
@@ -188,10 +193,10 @@ class HealthService:
|
||||
# @POST: Returns HealthSummaryResponse with counts and latest record row per dashboard.
|
||||
# @SIDE_EFFECT: May call Superset API to resolve dashboard metadata.
|
||||
# @DATA_CONTRACT: Input[environment_id: Optional[str]] -> Output[HealthSummaryResponse]
|
||||
# @RELATION: [CALLS] ->[self._prime_dashboard_meta_cache]
|
||||
# @RELATION: [CALLS] ->[self._resolve_dashboard_meta]
|
||||
# @RELATION: [CALLS] ->[_prime_dashboard_meta_cache]
|
||||
# @RELATION: [CALLS] ->[_resolve_dashboard_meta]
|
||||
async def get_health_summary(
|
||||
self, environment_id: str = None
|
||||
self, environment_id: str = ""
|
||||
) -> HealthSummaryResponse:
|
||||
"""
|
||||
@PURPOSE: Aggregates the latest validation status for all dashboards.
|
||||
@@ -228,7 +233,8 @@ class HealthService:
|
||||
unknown_count = 0
|
||||
|
||||
for rec in records:
|
||||
status = rec.status.upper()
|
||||
record = cast(Any, rec)
|
||||
status = str(record.status or "").upper()
|
||||
if status == "PASS":
|
||||
pass_count += 1
|
||||
elif status == "WARN":
|
||||
@@ -239,18 +245,34 @@ class HealthService:
|
||||
unknown_count += 1
|
||||
status = "UNKNOWN"
|
||||
|
||||
meta = self._resolve_dashboard_meta(rec.dashboard_id, rec.environment_id)
|
||||
record_id = str(record.id or "")
|
||||
dashboard_id = str(record.dashboard_id or "")
|
||||
resolved_environment_id = (
|
||||
str(record.environment_id)
|
||||
if record.environment_id is not None
|
||||
else None
|
||||
)
|
||||
response_environment_id = (
|
||||
resolved_environment_id
|
||||
if resolved_environment_id is not None
|
||||
else "unknown"
|
||||
)
|
||||
task_id = str(record.task_id) if record.task_id is not None else None
|
||||
summary = str(record.summary) if record.summary is not None else None
|
||||
timestamp = cast(Any, record.timestamp)
|
||||
|
||||
meta = self._resolve_dashboard_meta(dashboard_id, resolved_environment_id)
|
||||
items.append(
|
||||
DashboardHealthItem(
|
||||
record_id=rec.id,
|
||||
dashboard_id=rec.dashboard_id,
|
||||
record_id=record_id,
|
||||
dashboard_id=dashboard_id,
|
||||
dashboard_slug=meta.get("slug"),
|
||||
dashboard_title=meta.get("title"),
|
||||
environment_id=rec.environment_id or "unknown",
|
||||
environment_id=response_environment_id,
|
||||
status=status,
|
||||
last_check=rec.timestamp,
|
||||
task_id=rec.task_id,
|
||||
summary=rec.summary,
|
||||
last_check=timestamp,
|
||||
task_id=task_id,
|
||||
summary=summary,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -275,11 +297,9 @@ class HealthService:
|
||||
# @POST: Returns True only when a matching record was deleted.
|
||||
# @SIDE_EFFECT: Deletes DB rows, optional screenshot file, and optional task/log persistence.
|
||||
# @DATA_CONTRACT: Input[record_id: str, task_manager: Optional[TaskManager]] -> Output[bool]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.models.llm.ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[backend.src.core.task_manager.TaskManager]
|
||||
# @RELATION: [CALLS] ->[os.path.exists]
|
||||
# @RELATION: [CALLS] ->[os.remove]
|
||||
# @RELATION: [CALLS] ->[backend.src.core.task_manager.cleanup.TaskCleanupService.delete_task_with_logs]
|
||||
# @RELATION: [DEPENDS_ON] ->[ValidationRecord]
|
||||
# @RELATION: [DEPENDS_ON] ->[TaskManager]
|
||||
# @RELATION: [DEPENDS_ON] ->[TaskCleanupService]
|
||||
def delete_validation_report(
|
||||
self, record_id: str, task_manager: Optional[TaskManager] = None
|
||||
) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user