Remove JSON-based ad-hoc history storage and migrate to SQLAlchemy database backend with improved duration parsing and comprehensive CRUD operations

This commit is contained in:
2025-12-05 09:34:40 -05:00
parent ad3a8a5639
commit 123ca2cc08
14 changed files with 1237 additions and 1147 deletions
+9 -6
View File
@@ -33,12 +33,15 @@ DATABASE_URL = os.environ.get("DATABASE_URL", f"sqlite+aiosqlite:///{DEFAULT_DB_
def _ensure_sqlite_dir(db_url: str) -> None:
if not db_url.startswith("sqlite"):
return
parsed = urlparse(db_url.replace("sqlite+aiosqlite", "sqlite"))
if parsed.scheme != "sqlite":
return
db_path = Path(parsed.path)
if db_path.parent:
db_path.parent.mkdir(parents=True, exist_ok=True)
# Extraire le chemin après sqlite+aiosqlite:///
# Sur Windows, le chemin peut être C:\... donc on ne peut pas utiliser urlparse
prefix = "sqlite+aiosqlite:///"
if db_url.startswith(prefix):
path_str = db_url[len(prefix):]
# Sur Windows, le chemin peut commencer par une lettre de lecteur (C:)
db_path = Path(path_str)
if db_path.parent and str(db_path.parent) != ".":
db_path.parent.mkdir(parents=True, exist_ok=True)
DEFAULT_DB_PATH.parent.mkdir(parents=True, exist_ok=True)
_ensure_sqlite_dir(DATABASE_URL)
+14 -2
View File
@@ -1,9 +1,9 @@
from __future__ import annotations
from datetime import datetime
from typing import List, Optional
from typing import Any, Dict, List, Optional
from sqlalchemy import Boolean, DateTime, String, Text
from sqlalchemy import Boolean, DateTime, Integer, JSON, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql import func
@@ -15,18 +15,30 @@ class Schedule(Base):
id: Mapped[str] = mapped_column(String, primary_key=True)
name: Mapped[str] = mapped_column(String, nullable=False)
description: Mapped[Optional[str]] = mapped_column(Text)
playbook: Mapped[str] = mapped_column(String, nullable=False)
target_type: Mapped[Optional[str]] = mapped_column(String, default="group")
target: Mapped[str] = mapped_column(String, nullable=False)
extra_vars: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON)
schedule_type: Mapped[str] = mapped_column(String, nullable=False)
schedule_time: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
recurrence_type: Mapped[Optional[str]] = mapped_column(String)
recurrence_time: Mapped[Optional[str]] = mapped_column(String)
recurrence_days: Mapped[Optional[str]] = mapped_column(Text)
cron_expression: Mapped[Optional[str]] = mapped_column(String)
timezone: Mapped[Optional[str]] = mapped_column(String, default="America/Montreal")
start_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
end_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
tags: Mapped[Optional[str]] = mapped_column(Text)
next_run: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
last_run: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
last_status: Mapped[Optional[str]] = mapped_column(String, default="never")
retry_on_failure: Mapped[Optional[int]] = mapped_column(Integer, default=0)
timeout: Mapped[Optional[int]] = mapped_column(Integer, default=3600)
run_count: Mapped[Optional[int]] = mapped_column(Integer, default=0)
success_count: Mapped[Optional[int]] = mapped_column(Integer, default=0)
failure_count: Mapped[Optional[int]] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
+1
View File
@@ -20,6 +20,7 @@ class ScheduleRun(Base):
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
duration: Mapped[Optional[float]] = mapped_column(Float)
hosts_impacted: Mapped[Optional[int]] = mapped_column(Integer, default=0)
error_message: Mapped[Optional[str]] = mapped_column(Text)
output: Mapped[Optional[str]] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())