feat: detect visible wordcloud archive sources
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
"""Helpers for locating and safely archiving word-cloud layout databases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import sqlite3
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WordcloudSource:
|
||||
asset_id: str
|
||||
source_job_id: str
|
||||
|
||||
|
||||
def find_visible_wordcloud_sources(
|
||||
document: dict[str, Any], load_asset_meta: Callable[[str], dict[str, Any]]
|
||||
) -> list[WordcloudSource]:
|
||||
"""Return unique word-cloud sources referenced by visible sticker elements."""
|
||||
visible = {
|
||||
str(layer.get("id")): layer.get("visible") is not False
|
||||
for layer in document.get("layers") or []
|
||||
if isinstance(layer, dict)
|
||||
}
|
||||
seen: set[str] = set()
|
||||
result: list[WordcloudSource] = []
|
||||
for element in document.get("elements") or []:
|
||||
if not isinstance(element, dict) or element.get("type") != "sticker":
|
||||
continue
|
||||
if visible and visible.get(str(element.get("layerId")), True) is False:
|
||||
continue
|
||||
asset_id = str(element.get("assetId") or "")
|
||||
meta = load_asset_meta(asset_id)
|
||||
job_id = str(meta.get("job_id") or "")
|
||||
if meta.get("type") == "wordcloud" and job_id and job_id not in seen:
|
||||
seen.add(job_id)
|
||||
result.append(WordcloudSource(asset_id, job_id))
|
||||
return result
|
||||
|
||||
|
||||
def validate_word_locations_db(db_path: Path) -> None:
|
||||
"""Ensure a read-only SQLite database contains the expected layout table."""
|
||||
if not db_path.is_file():
|
||||
raise ValueError(f"word locations database does not exist: {db_path}")
|
||||
try:
|
||||
with sqlite3.connect(f"file:{db_path.resolve()}?mode=ro", uri=True) as connection:
|
||||
exists = connection.execute(
|
||||
"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'word_locations'"
|
||||
).fetchone()
|
||||
except sqlite3.Error as exc:
|
||||
raise ValueError(f"invalid word locations database: {db_path}") from exc
|
||||
if exists is None:
|
||||
raise ValueError(f"word locations database is missing word_locations: {db_path}")
|
||||
|
||||
|
||||
def copy_word_locations_snapshot(source: Path, destination: Path) -> str:
|
||||
"""Copy a validated layout database atomically and return its SHA-256 checksum."""
|
||||
validate_word_locations_db(source)
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
temporary = destination.with_name(f"{destination.name}.tmp")
|
||||
digest = hashlib.sha256()
|
||||
try:
|
||||
with source.open("rb") as source_file, temporary.open("wb") as temporary_file:
|
||||
while chunk := source_file.read(1024 * 1024):
|
||||
temporary_file.write(chunk)
|
||||
digest.update(chunk)
|
||||
validate_word_locations_db(temporary)
|
||||
temporary.replace(destination)
|
||||
except Exception:
|
||||
temporary.unlink(missing_ok=True)
|
||||
raise
|
||||
return digest.hexdigest()
|
||||
Reference in New Issue
Block a user