show completion in frontend

This commit is contained in:
2026-07-15 11:18:38 -04:00
parent 425d418426
commit b3116f60e2
11 changed files with 265 additions and 39 deletions
+24 -3
View File
@@ -452,21 +452,42 @@ def get_holidays() -> dict[str, Any]:
return {"dates": sorted(db.list_holidays(DB_PATH))}
@app.get("/api/schedule/versions")
def get_schedule_versions() -> dict[str, Any]:
return {"versions": db.get_schedule_versions(DB_PATH)}
@app.get("/api/schedule/week")
def get_schedule_week(start: str | None = None) -> dict[str, Any]:
def get_schedule_week(start: str | None = None, version: int | None = None) -> dict[str, Any]:
week_start = start or date.today().isoformat()
try:
datetime.strptime(week_start, "%Y-%m-%d")
except ValueError as exc:
raise HTTPException(status_code=400, detail="start must be YYYY-MM-DD") from exc
if version is not None and version <= 0:
raise HTTPException(status_code=400, detail="version must be a positive integer")
week_start_date = datetime.strptime(week_start, "%Y-%m-%d").date()
rows = db.get_schedule_week(week_start, DB_PATH)
all_rows = db.get_schedule_rows_for_latest_version(DB_PATH)
selected_version = db.resolve_schedule_version(version, DB_PATH)
if version is not None and selected_version is None:
raise HTTPException(status_code=404, detail=f"Schedule version {version} was not found")
rows = db.get_schedule_week(week_start, selected_version, DB_PATH)
# Log completed tests on the current schedule
completed_count = sum(1 for row in rows if row.status == 'completed')
if completed_count > 0:
print(f"[api] /schedule/week: {completed_count} test(s) marked as completed on calendar")
for row in rows:
if row.status == 'completed':
print(f" - {row.test_id} on {row.device} (scheduled {row.scheduled_date})")
all_rows = db.get_schedule_rows(selected_version, DB_PATH)
completion_date = max((row.scheduled_date for row in all_rows), default=None)
holiday_dates = db.list_holidays(DB_PATH)
return {
"start_date": week_start,
"schedule_version": selected_version,
"items": [_serialize_schedule_row(row) for row in rows],
"total_scheduled_tests": len(all_rows),
"completion_date": completion_date,
+106 -22
View File
@@ -471,11 +471,69 @@ def create_schedule_version(
return next_version
def get_schedule_week(start_date: str, db_path: str | Path = DB_PATH) -> list[ScheduleRow]:
def get_schedule_versions(db_path: str | Path = DB_PATH) -> list[dict[str, Any]]:
with get_connection(db_path) as conn:
version_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = version_row["latest"]
if latest is None:
rows = conn.execute(
"""
SELECT
schedule_version,
MIN(created_at) AS created_at,
COUNT(*) AS entry_count
FROM schedules
GROUP BY schedule_version
ORDER BY schedule_version DESC
"""
).fetchall()
return [
{
"schedule_version": int(row["schedule_version"]),
"created_at": row["created_at"],
"entry_count": int(row["entry_count"]),
}
for row in rows
]
def resolve_schedule_version(
version: int | None = None,
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
def get_schedule_week(
start_date: str,
version: int | None = None,
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)
if selected_version is None:
return []
rows = conn.execute(
@@ -514,7 +572,7 @@ def get_schedule_week(start_date: str, db_path: str | Path = DB_PATH) -> list[Sc
ORDER BY s.scheduled_date, s.shift_index, s.sequence_in_shift
""",
(
latest,
selected_version,
start_date,
start_date,
start_date,
@@ -544,11 +602,27 @@ def get_schedule_week(start_date: str, db_path: str | Path = DB_PATH) -> list[Sc
return result
def get_schedule_rows_for_latest_version(db_path: str | Path = DB_PATH) -> list[ScheduleRow]:
def get_schedule_rows(
version: int | None = None,
db_path: str | Path = DB_PATH,
) -> list[ScheduleRow]:
with get_connection(db_path) as conn:
version_row = conn.execute("SELECT MAX(schedule_version) AS latest FROM schedules").fetchone()
latest = version_row["latest"]
if latest is None:
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)
if selected_version is None:
return []
rows = conn.execute(
@@ -570,7 +644,7 @@ def get_schedule_rows_for_latest_version(db_path: str | Path = DB_PATH) -> list[
WHERE s.schedule_version = ?
ORDER BY s.scheduled_date, s.shift_index, s.sequence_in_shift
""",
(latest,),
(selected_version,),
).fetchall()
result: list[ScheduleRow] = []
@@ -593,21 +667,31 @@ def get_schedule_rows_for_latest_version(db_path: str | Path = DB_PATH) -> list[
return result
def mark_tests_completed(test_ids_with_device: list[tuple[str, str]], db_path: str | Path = DB_PATH) -> None:
def mark_tests_completed(test_ids_with_device: list[tuple[str, str]], db_path: str | Path = DB_PATH) -> int:
if not test_ids_with_device:
return
return 0
with get_connection(db_path) as conn:
conn.executemany(
"""
UPDATE tests
SET status = 'completed', updated_at = CURRENT_TIMESTAMP
WHERE test_id = ?
AND device = ?
AND status != 'completed'
""",
test_ids_with_device,
)
# executemany() doesn't properly return rowcount, so we track manually
total_updated = 0
for test_id, device in test_ids_with_device:
cursor = conn.execute(
"""
UPDATE tests
SET status = 'completed', updated_at = CURRENT_TIMESTAMP
WHERE test_id = ?
AND device = ?
AND status != 'completed'
""",
(test_id, device),
)
if cursor.rowcount > 0:
total_updated += cursor.rowcount
print(f"[db] marked {test_id} on {device} as completed")
else:
print(f"[db] {test_id} on {device}: no update (not found or already completed)")
return total_updated
def mark_overdue_as_rerun(db_path: str | Path = DB_PATH) -> int:
+9 -1
View File
@@ -283,6 +283,7 @@ def scan_results(results_dir_dut, results_dir_ref, smb_credentials=None):
entry.name
for entry in _iter_dir_entries(results_dir_ref, smb_credentials=smb_credentials)
if entry.is_dir()
and not entry.name.startswith("obsolete")
]
except OSError as exc:
print(f"[scanner] Cannot read results dir: {exc}")
@@ -310,7 +311,14 @@ def scan_results(results_dir_dut, results_dir_ref, smb_credentials=None):
if unmatched_entries:
print(f"[scanner] skipped {len(unmatched_entries)} result dir(s) with no recognizable test id")
mark_tests_completed(completed_batch)
if completed_batch:
print(f"[scanner] marking {len(completed_batch)} test(s) as completed:")
for test_id, device in completed_batch:
print(f" - {test_id} on {device}")
updated_count = mark_tests_completed(completed_batch)
print(f"[scanner] {updated_count} test(s) actually updated in database")
newly_rerun = mark_overdue_as_rerun()
if newly_rerun:
print(f"[scanner] {newly_rerun} test(s) marked as rerun-required (scheduled but not completed)")