Web UI: setup progress + logs folding, Finished Decks library, commander search UX (debounce, keyboard, highlights, color chips), ranking fixes (first-word priority, substring include), optional auto-select; setup start reliability (POST+GET), force runs, status with percent/ETA/timestamps; stepwise builder with added stage reporting and sidecar summaries; keyboard grid wrap-around; restrict commander search to eligible rows
2025-08-26 09:48:25 -07:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import threading
|
|
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Request
|
|
|
|
from fastapi import Body
|
|
|
|
from pathlib import Path
|
|
|
|
import json as _json
|
|
|
|
from fastapi.responses import HTMLResponse, JSONResponse
|
|
|
|
from ..app import templates
|
|
|
|
from ..services.orchestrator import _ensure_setup_ready # type: ignore
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/setup")
|
|
|
|
|
|
|
|
|
|
|
|
def _kickoff_setup_async(force: bool = False):
|
feat(editorial): Phase D synergy commander enrichment, augmentation, lint & docs\n\nAdds Phase D editorial tooling: synergy-based commander selection with 3/2/1 pattern, duplicate filtering, annotated synergy_commanders, promotion to minimum examples, and augmentation heuristics (e.g. Counters Matter/Proliferate injection). Includes new scripts (generate_theme_editorial_suggestions, lint, validate, catalog build/apply), updates orchestrator & web routes, expands CI workflow, and documents usage & non-determinism policies. Updates lint rules, type definitions, and docker configs.
2025-09-18 10:59:20 -07:00
|
|
|
"""Start setup/tagging in a background thread.
|
|
|
|
|
|
|
|
Previously we passed a no-op output function, which hid downstream steps (e.g., theme export).
|
|
|
|
Using print provides visibility in container logs and helps diagnose export issues.
|
|
|
|
"""
|
Web UI: setup progress + logs folding, Finished Decks library, commander search UX (debounce, keyboard, highlights, color chips), ranking fixes (first-word priority, substring include), optional auto-select; setup start reliability (POST+GET), force runs, status with percent/ETA/timestamps; stepwise builder with added stage reporting and sidecar summaries; keyboard grid wrap-around; restrict commander search to eligible rows
2025-08-26 09:48:25 -07:00
|
|
|
def runner():
|
|
|
|
try:
|
feat(editorial): Phase D synergy commander enrichment, augmentation, lint & docs\n\nAdds Phase D editorial tooling: synergy-based commander selection with 3/2/1 pattern, duplicate filtering, annotated synergy_commanders, promotion to minimum examples, and augmentation heuristics (e.g. Counters Matter/Proliferate injection). Includes new scripts (generate_theme_editorial_suggestions, lint, validate, catalog build/apply), updates orchestrator & web routes, expands CI workflow, and documents usage & non-determinism policies. Updates lint rules, type definitions, and docker configs.
2025-09-18 10:59:20 -07:00
|
|
|
_ensure_setup_ready(print, force=force) # type: ignore[arg-type]
|
|
|
|
except Exception as e: # pragma: no cover - background best effort
|
|
|
|
try:
|
|
|
|
print(f"Setup thread failed: {e}")
|
|
|
|
except Exception:
|
|
|
|
pass
|
Web UI: setup progress + logs folding, Finished Decks library, commander search UX (debounce, keyboard, highlights, color chips), ranking fixes (first-word priority, substring include), optional auto-select; setup start reliability (POST+GET), force runs, status with percent/ETA/timestamps; stepwise builder with added stage reporting and sidecar summaries; keyboard grid wrap-around; restrict commander search to eligible rows
2025-08-26 09:48:25 -07:00
|
|
|
t = threading.Thread(target=runner, daemon=True)
|
|
|
|
t.start()
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/running", response_class=HTMLResponse)
|
|
|
|
async def setup_running(request: Request, start: Optional[int] = 0, next: Optional[str] = None, force: Optional[bool] = None) -> HTMLResponse: # type: ignore[override]
|
|
|
|
# Optionally start the setup/tagging in the background if requested
|
|
|
|
try:
|
|
|
|
if start and int(start) != 0:
|
|
|
|
# honor optional force flag from query
|
|
|
|
f = False
|
|
|
|
try:
|
|
|
|
if force is not None:
|
|
|
|
f = bool(force)
|
|
|
|
else:
|
|
|
|
q_force = request.query_params.get('force')
|
|
|
|
if q_force is not None:
|
|
|
|
f = q_force.strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
except Exception:
|
|
|
|
f = False
|
|
|
|
_kickoff_setup_async(force=f)
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
return templates.TemplateResponse("setup/running.html", {"request": request, "next_url": next})
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/start")
|
|
|
|
async def setup_start(request: Request, force: bool = Body(False)): # accept JSON body {"force": true}
|
|
|
|
try:
|
|
|
|
# Allow query string override as well (?force=1)
|
|
|
|
try:
|
|
|
|
q_force = request.query_params.get('force')
|
|
|
|
if q_force is not None:
|
|
|
|
force = q_force.strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
# Write immediate status so UI reflects the start
|
|
|
|
try:
|
|
|
|
p = Path("csv_files")
|
|
|
|
p.mkdir(parents=True, exist_ok=True)
|
|
|
|
status = {"running": True, "phase": "setup", "message": "Starting setup/tagging...", "color": None}
|
|
|
|
with (p / ".setup_status.json").open('w', encoding='utf-8') as f:
|
|
|
|
_json.dump(status, f)
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
_kickoff_setup_async(force=bool(force))
|
|
|
|
return JSONResponse({"ok": True, "started": True, "force": bool(force)}, status_code=202)
|
|
|
|
except Exception:
|
|
|
|
return JSONResponse({"ok": False}, status_code=500)
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/start")
|
|
|
|
async def setup_start_get(request: Request):
|
|
|
|
"""GET alias to start setup/tagging via query string (?force=1).
|
|
|
|
|
|
|
|
Useful as a fallback from clients that cannot POST JSON.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
# Determine force from query params
|
|
|
|
force = False
|
|
|
|
try:
|
|
|
|
q_force = request.query_params.get('force')
|
|
|
|
if q_force is not None:
|
|
|
|
force = q_force.strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
# Write immediate status so UI reflects the start
|
|
|
|
try:
|
|
|
|
p = Path("csv_files")
|
|
|
|
p.mkdir(parents=True, exist_ok=True)
|
|
|
|
status = {"running": True, "phase": "setup", "message": "Starting setup/tagging...", "color": None}
|
|
|
|
with (p / ".setup_status.json").open('w', encoding='utf-8') as f:
|
|
|
|
_json.dump(status, f)
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
_kickoff_setup_async(force=bool(force))
|
|
|
|
return JSONResponse({"ok": True, "started": True, "force": bool(force)}, status_code=202)
|
|
|
|
except Exception:
|
|
|
|
return JSONResponse({"ok": False}, status_code=500)
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/", response_class=HTMLResponse)
|
|
|
|
async def setup_index(request: Request) -> HTMLResponse:
|
|
|
|
return templates.TemplateResponse("setup/index.html", {"request": request})
|