feat: extend media roles and acceptance coverage
This commit is contained in:
@@ -18,6 +18,13 @@ DERIVATIVE_TARGETS = {"card": 256, "detail": 1024}
|
||||
WATERBODY_MEDIA_ROLES = frozenset({
|
||||
"waterbody_cover", "waterbody_map", "waterbody_depth_map", "waterbody_screenshot",
|
||||
})
|
||||
TACKLE_MEDIA_ROLES = frozenset({
|
||||
"tackle_card", "tackle_detail", "rig_diagram", "tackle_screenshot",
|
||||
})
|
||||
MEDIA_ROLES_BY_ENTITY = {
|
||||
"waterbody": WATERBODY_MEDIA_ROLES,
|
||||
"tackle": TACKLE_MEDIA_ROLES,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -276,8 +283,8 @@ def review_asset(
|
||||
item = next((asset for asset in manifest.get("assets", []) if asset["asset_url"] == asset_url), None)
|
||||
if item is None:
|
||||
raise ValueError("asset URL is not present in manifest")
|
||||
if media_role is not None and (entity_type != "waterbody" or media_role not in WATERBODY_MEDIA_ROLES):
|
||||
raise ValueError("media role is only valid for a waterbody and must be a known waterbody role")
|
||||
if media_role is not None and media_role not in MEDIA_ROLES_BY_ENTITY.get(entity_type or "", set()):
|
||||
raise ValueError(f"media role is only valid for {entity_type}; use a known {entity_type} role")
|
||||
if decision == "approved":
|
||||
if item.get("status") != "stored":
|
||||
raise ValueError("only a stored asset can be approved")
|
||||
@@ -345,6 +352,10 @@ def audit_media_catalog(root: Path) -> dict:
|
||||
issues.append(f"{item['asset_url']}: derivative {exc}")
|
||||
if status == "approved" and (not item.get("entity_key") or item.get("entity_type") not in {"fish", "waterbody", "tackle", "reference"}):
|
||||
issues.append(f"{item['asset_url']}: approved asset has no valid canonical mapping")
|
||||
if item.get("media_role") not in {None, *WATERBODY_MEDIA_ROLES, *TACKLE_MEDIA_ROLES}:
|
||||
issues.append(f"{item['asset_url']}: unknown media role")
|
||||
elif item.get("media_role") is not None and item.get("media_role") not in MEDIA_ROLES_BY_ENTITY.get(item.get("entity_type"), set()):
|
||||
issues.append(f"{item['asset_url']}: media role does not match entity type")
|
||||
files_root = root / "files"
|
||||
orphaned = sorted(str(path.relative_to(root)) for path in files_root.rglob("*") if path.is_file() and str(path.relative_to(root)) not in referenced) if files_root.exists() else []
|
||||
return {"total": sum(statuses.values()), "statuses": statuses, "issues": issues, "orphaned_files": orphaned}
|
||||
|
||||
@@ -192,6 +192,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
parser.add_argument("--decision", choices=("approved", "rejected"))
|
||||
parser.add_argument("--entity-type", choices=("fish", "waterbody", "tackle", "reference"))
|
||||
parser.add_argument("--entity-key")
|
||||
parser.add_argument("--media-role", help="Explicit reviewed role for waterbody/tackle media")
|
||||
parser.add_argument("--note")
|
||||
parser.add_argument("--audit", action="store_true", help="Verify manifest metadata, hashes and local files without network access")
|
||||
parser.add_argument("--coverage", action="store_true", help="Compare candidates and approvals with the catalog baseline")
|
||||
@@ -232,7 +233,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
if args.review_url:
|
||||
if not args.decision:
|
||||
parser.error("--decision is required with --review-url")
|
||||
item = review_asset(args.root / "manifest.json", asset_url=args.review_url, decision=args.decision, entity_type=args.entity_type, entity_key=args.entity_key, note=args.note)
|
||||
item = review_asset(args.root / "manifest.json", asset_url=args.review_url, decision=args.decision, entity_type=args.entity_type, entity_key=args.entity_key, note=args.note, media_role=args.media_role)
|
||||
print(f"{item['status']} {item['asset_url']}")
|
||||
return 0
|
||||
if args.reclassify:
|
||||
|
||||
Reference in New Issue
Block a user