showing previous version schedule as faded

This commit is contained in:
2026-07-20 14:16:56 -04:00
parent a49772aece
commit 5eebee70d8
8 changed files with 293 additions and 91 deletions
+120 -87
View File
@@ -19,6 +19,7 @@ REF = os.getenv("REF", "CGW452").strip()
# Device names for the test database (use hardware device names)
DEVICE_DUT = DUT
DEVICE_REF = REF
MAX_SCHEDULE_VERSIONS = 50
@dataclass(frozen=True)
class TestRecord:
@@ -139,6 +140,20 @@ def init_db(db_path: str | Path = DB_PATH) -> None:
_ensure_column(conn, "tests", "excluded", "INTEGER NOT NULL DEFAULT 0")
_ensure_column(conn, "tests", "throttled", "INTEGER NOT NULL DEFAULT 0")
_ensure_column(conn, "tests", "station_testpoint_map", "TEXT")
_ensure_column(conn, "schedules", "status_snapshot", "TEXT")
conn.execute(
"""
UPDATE schedules
SET status_snapshot = COALESCE((
SELECT t.status
FROM tests t
WHERE t.test_id = schedules.test_id
AND t.device = schedules.device
), 'pending')
WHERE status_snapshot IS NULL
"""
)
prune_schedule_versions(conn=conn)
# Seed runtime defaults for schedule estimation.
conn.execute(
@@ -454,7 +469,7 @@ def list_holidays(db_path: str | Path = DB_PATH) -> set[str]:
def create_schedule_version(
entries: list[tuple[str, str, str, int, int]],
entries: list[tuple[str, str, str, int, int, str]],
db_path: str | Path = DB_PATH,
) -> int:
with get_connection(db_path) as conn:
@@ -464,18 +479,102 @@ def create_schedule_version(
if entries:
conn.executemany(
"""
INSERT INTO schedules(schedule_version, test_id, device, scheduled_date, shift_index, sequence_in_shift)
VALUES (?, ?, ?, ?, ?, ?)
INSERT INTO schedules(
schedule_version,
test_id,
device,
scheduled_date,
shift_index,
sequence_in_shift,
status_snapshot
)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
[
(next_version, test_id, device, scheduled_date, shift_index, sequence)
for test_id, device, scheduled_date, shift_index, sequence in entries
(next_version, test_id, device, scheduled_date, shift_index, sequence, status_snapshot)
for test_id, device, scheduled_date, shift_index, sequence, status_snapshot in entries
],
)
prune_schedule_versions(conn=conn)
return next_version
def prune_schedule_versions(
max_versions: int = MAX_SCHEDULE_VERSIONS,
db_path: str | Path = DB_PATH,
conn: sqlite3.Connection | None = None,
) -> int:
if max_versions <= 0:
raise ValueError("max_versions must be positive")
def _prune(active_conn: sqlite3.Connection) -> int:
cursor = active_conn.execute(
"""
DELETE FROM schedules
WHERE schedule_version IN (
SELECT schedule_version
FROM (
SELECT schedule_version
FROM schedules
GROUP BY schedule_version
ORDER BY schedule_version DESC
LIMIT -1 OFFSET ?
)
)
""",
(max_versions,),
)
return cursor.rowcount
if conn is not None:
return _prune(conn)
with get_connection(db_path) as active_conn:
return _prune(active_conn)
def _resolve_requested_schedule_version(
conn: sqlite3.Connection,
version: int | None,
) -> tuple[int | None, int | None]:
latest_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = latest_row["latest"]
latest_version = int(latest) if latest is not None else None
if version is not None:
row = conn.execute(
"SELECT 1 AS exists_row FROM schedules WHERE schedule_version = ? LIMIT 1",
(version,),
).fetchone()
return (int(version) if row else None, latest_version)
return latest_version, latest_version
def _hydrate_schedule_rows(rows: list[sqlite3.Row]) -> list[ScheduleRow]:
result: list[ScheduleRow] = []
for row in rows:
result.append(
ScheduleRow(
test_id=row["test_id"],
device=row["device"],
scheduled_date=row["scheduled_date"],
shift_index=int(row["shift_index"]),
sequence_in_shift=int(row["sequence_in_shift"]),
test_type=row["test_type"],
rotation=row["rotation"],
config=json.loads(row["config_json"] or "{}"),
status=row["status"],
priority=int(row["priority"]),
estimated_minutes=int(row["estimated_minutes"]),
)
)
return result
def get_schedule_versions(db_path: str | Path = DB_PATH) -> list[dict[str, Any]]:
with get_connection(db_path) as conn:
rows = conn.execute(
@@ -505,16 +604,8 @@ def resolve_schedule_version(
db_path: str | Path = DB_PATH,
) -> int | None:
with get_connection(db_path) as conn:
if version is not None:
row = conn.execute(
"SELECT 1 FROM schedules WHERE schedule_version = ? LIMIT 1",
(version,),
).fetchone()
return int(version) if row else None
latest_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = latest_row["latest"]
return int(latest) if latest is not None else None
selected_version, _latest_version = _resolve_requested_schedule_version(conn, version)
return selected_version
def get_schedule_week(
@@ -523,26 +614,15 @@ def get_schedule_week(
db_path: str | Path = DB_PATH,
) -> list[ScheduleRow]:
with get_connection(db_path) as conn:
if version is not None:
version_row = conn.execute(
"SELECT 1 AS exists_row FROM schedules WHERE schedule_version = ? LIMIT 1",
(version,),
).fetchone()
if version_row is None:
return []
selected_version = int(version)
else:
latest_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = latest_row["latest"]
if latest is None:
return []
selected_version = int(latest)
selected_version, latest_version = _resolve_requested_schedule_version(conn, version)
if selected_version is None:
return []
use_live_status = latest_version is not None and selected_version == latest_version
status_sql = "COALESCE(t.status, s.status_snapshot, 'pending')" if use_live_status else "COALESCE(s.status_snapshot, 'pending')"
rows = conn.execute(
"""
f"""
SELECT
s.test_id,
s.device,
@@ -552,7 +632,7 @@ def get_schedule_week(
t.test_type,
t.rotation,
t.config_json,
t.status,
{status_sql} AS status,
t.priority,
t.estimated_minutes
FROM schedules s
@@ -586,25 +666,7 @@ def get_schedule_week(
),
).fetchall()
result: list[ScheduleRow] = []
for row in rows:
result.append(
ScheduleRow(
test_id=row["test_id"],
device=row["device"],
scheduled_date=row["scheduled_date"],
shift_index=int(row["shift_index"]),
sequence_in_shift=int(row["sequence_in_shift"]),
test_type=row["test_type"],
rotation=row["rotation"],
config=json.loads(row["config_json"] or "{}"),
status=row["status"],
priority=int(row["priority"]),
estimated_minutes=int(row["estimated_minutes"]),
)
)
return result
return _hydrate_schedule_rows(rows)
def get_schedule_rows(
@@ -612,26 +674,15 @@ def get_schedule_rows(
db_path: str | Path = DB_PATH,
) -> list[ScheduleRow]:
with get_connection(db_path) as conn:
if version is not None:
version_row = conn.execute(
"SELECT 1 AS exists_row FROM schedules WHERE schedule_version = ? LIMIT 1",
(version,),
).fetchone()
if version_row is None:
return []
selected_version = int(version)
else:
latest_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = latest_row["latest"]
if latest is None:
return []
selected_version = int(latest)
selected_version, latest_version = _resolve_requested_schedule_version(conn, version)
if selected_version is None:
return []
use_live_status = latest_version is not None and selected_version == latest_version
status_sql = "COALESCE(t.status, s.status_snapshot, 'pending')" if use_live_status else "COALESCE(s.status_snapshot, 'pending')"
rows = conn.execute(
"""
f"""
SELECT
s.test_id,
s.device,
@@ -641,7 +692,7 @@ def get_schedule_rows(
t.test_type,
t.rotation,
t.config_json,
t.status,
{status_sql} AS status,
t.priority,
t.estimated_minutes
FROM schedules s
@@ -652,25 +703,7 @@ def get_schedule_rows(
(selected_version,),
).fetchall()
result: list[ScheduleRow] = []
for row in rows:
result.append(
ScheduleRow(
test_id=row["test_id"],
device=row["device"],
scheduled_date=row["scheduled_date"],
shift_index=int(row["shift_index"]),
sequence_in_shift=int(row["sequence_in_shift"]),
test_type=row["test_type"],
rotation=row["rotation"],
config=json.loads(row["config_json"] or "{}"),
status=row["status"],
priority=int(row["priority"]),
estimated_minutes=int(row["estimated_minutes"]),
)
)
return result
return _hydrate_schedule_rows(rows)
def reset_completed_to_pending(db_path: str | Path = DB_PATH) -> int:
"""Reset all 'completed' tests back to 'pending'. Used before a full directory rescan."""