implemented watcher and rerun logic

This commit is contained in:
2026-06-25 11:31:20 -04:00
parent 21402f7ee3
commit be13849a4f
15 changed files with 871 additions and 97 deletions
+67 -4
View File
@@ -12,6 +12,7 @@ import db
import graph
from parser import CsvValidationError, parse_target_csv
from scheduler import SchedulerTest, compile_schedule, remove_from_active, reset_scheduler_state
from watcher import configure_result_watcher, stop_result_watcher
APP_ROOT = Path(__file__).resolve().parent
@@ -28,6 +29,41 @@ class SaveSettingsRequest(BaseModel):
settings: dict[str, Any]
def _smb_credentials_from_settings(settings: dict[str, Any]) -> dict[str, str]:
return {
"username": str(settings.get("smbUsername") or "").strip(),
"password": str(settings.get("smbPassword") or ""),
"domain": str(settings.get("smbDomain") or "").strip(),
}
def _runtime_overrides_from_settings(settings: dict[str, Any]) -> dict[str, int]:
def _parse_positive_int(value: Any) -> int | None:
if value is None:
return None
if isinstance(value, str):
value = value.strip()
if not value:
return None
try:
parsed = int(value)
except (TypeError, ValueError):
return None
return parsed if parsed > 0 else None
overrides: dict[str, int] = {}
for test_type, key in (
("P2P", "p2pRuntimeMinutes"),
("COE", "coeRuntimeMinutes"),
("P3P", "p3pRuntimeMinutes"),
):
minutes = _parse_positive_int(settings.get(key))
if minutes is not None:
overrides[test_type] = minutes
return overrides
class CompileScheduleRequest(BaseModel):
start_date: str | None = Field(default=None, description="YYYY-MM-DD")
rule: str = ""
@@ -48,7 +84,12 @@ class SaveHolidaysRequest(BaseModel):
async def lifespan(application: FastAPI):
db.init_db(DB_PATH)
graph.reset_graph_state()
yield
settings = db.read_settings(DB_PATH)
configure_result_watcher(settings)
try:
yield
finally:
stop_result_watcher()
app = FastAPI(title="Scheduler API", version="0.1.0", lifespan=lifespan)
@@ -69,6 +110,7 @@ def health() -> dict[str, str]:
@app.post("/api/settings")
def save_settings(request: SaveSettingsRequest) -> dict[str, str]:
db.save_settings(request.settings, DB_PATH)
configure_result_watcher(db.read_settings(DB_PATH))
return {"status": "saved"}
@@ -83,8 +125,16 @@ def load_tests(request: LoadTestsRequest) -> dict[str, Any]:
if not csv_path.is_absolute():
csv_path = APP_ROOT / csv_path
settings = db.read_settings(DB_PATH)
smb_credentials = _smb_credentials_from_settings(settings)
runtime_overrides = _runtime_overrides_from_settings(settings)
try:
parsed = parse_target_csv(csv_path)
parsed = parse_target_csv(
csv_path,
smb_credentials=smb_credentials,
runtime_overrides=runtime_overrides,
)
except FileNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
except CsvValidationError as exc:
@@ -94,7 +144,7 @@ def load_tests(request: LoadTestsRequest) -> dict[str, Any]:
reset_scheduler_state()
graph.reset_graph_state()
all_dut_tests = db.list_tests_for_device(DUT, DB_PATH)
graph.build_graph_once(all_dut_tests)
graph.build_and_persist_graph(all_dut_tests, DB_PATH)
return {
"loaded_tests": count,
@@ -117,6 +167,7 @@ def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]
except ValueError as exc:
raise HTTPException(status_code=400, detail="start_date must be YYYY-MM-DD") from exc
reset_scheduler_state()
stored_tests = db.list_schedulable_tests(DB_PATH, rule=request.rule)
if not stored_tests:
version = db.create_schedule_version([], DB_PATH)
@@ -144,6 +195,10 @@ def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]
for t in stored_tests
]
# DB-backed graph retrieval ensures compile works after restart without manual save/load.
all_dut_tests = db.list_tests_for_device(DUT, DB_PATH)
graph.get_graph(DB_PATH, all_dut_tests)
holiday_dates = db.list_holidays(DB_PATH)
try:
entries, completion_date = compile_schedule(
@@ -161,7 +216,7 @@ def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]
[(e.test_id, e.device, e.scheduled_date, e.shift_index, e.sequence_in_shift) for e in entries],
DB_PATH,
)
print(f"Schedule version {version} created with {len(entries)} entries, completion date: {completion_date}")
return {
"schedule_version": version,
"scheduled_tests": len(entries),
@@ -169,6 +224,14 @@ def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]
}
@app.get("/api/tests/rerun")
def get_rerun_tests() -> dict[str, Any]:
db.mark_overdue_as_rerun(DB_PATH)
tests = db.get_rerun_tests(DB_PATH)
total_minutes = sum(t["estimated_minutes"] for t in tests)
return {"tests": tests, "total_estimated_minutes": total_minutes}
@app.post("/api/holidays")
def save_holidays(request: SaveHolidaysRequest) -> dict[str, Any]:
dates = [d.strip() for d in request.dates if d.strip()]