← Files VeraARCHIVED FILE
modules/registro-imprese-sari/scripts/register_official_source.py
9.27 KB · Oct 3, 2026 · 06:30 UTC
"""Register a browser-selected or user-provided official source without fetching it."""
from __future__ import annotations
import argparse
import logging
import re
from pathlib import Path
from typing import Any
from case_core import (
PLUGIN_NAME,
AssuranceContractError,
ensure_safe_output_dir,
iso_now,
load_json_object,
load_running_case_context,
require_case_artifact_run,
safe_identifier,
sha256_bytes,
validate_iso_date,
validate_official_source_url,
write_private_bytes,
write_private_json,
)
__all__ = ["register_source", "main"]
LOGGER = logging.getLogger(__name__)
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
MAX_SNAPSHOT_BYTES = 10_000_000
ALLOWED_SOURCE_TYPES = {
"official_sari_selected_result",
"official_cciaa_guidance",
"official_dire_guidance",
"official_registro_imprese_guidance",
"official_inps_guidance",
"official_ivass_guidance",
"official_suap_guidance",
"official_other",
}
ALLOWED_AUTHORIZATION_BASES = {
"browser_assisted_metadata",
"user_provided_copy",
"written_reuse_authorization",
}
SAFE_FILENAME_RE = re.compile(r"[^A-Za-z0-9._-]+")
def _record_source_registration(
output_dir: Path,
*,
run_id: str,
source: dict[str, Any],
) -> None:
run_path = output_dir / "run_intake.json"
if not run_path.exists():
return
run = load_json_object(run_path)
if run.get("plugin") != PLUGIN_NAME or run.get("run_id") != run_id:
raise ValueError("run_intake.json belongs to another run")
trace = run.get("execution_trace")
if not isinstance(trace, list):
trace = []
trace.append(
{
"step_id": f"register_official_source_{len(trace) + 1}",
"kind": "local_source_registration",
"command": [
"python",
"scripts/register_official_source.py",
"--source-id",
source["source_id"],
"--authorization-basis",
source["authorization_basis"],
],
"execution_location": "local_python",
"status": "passed",
"inputs": [source["official_url"]],
"outputs": ["official_sources.json"],
}
)
run["execution_trace"] = trace
write_private_json(run_path, run)
def _manifest(output_dir: Path, *, run_id: str) -> dict[str, Any]:
path = output_dir / "official_sources.json"
if not path.exists():
return {
"schema_version": "1.0",
"plugin": PLUGIN_NAME,
"run_id": run_id,
"created_at": iso_now(),
"sources": [],
"source_count": 0,
}
payload = load_json_object(path)
if payload.get("plugin") != PLUGIN_NAME or payload.get("run_id") != run_id:
raise ValueError("existing official_sources.json belongs to another run")
if not isinstance(payload.get("sources"), list):
raise ValueError("existing official_sources.json has invalid sources")
return payload
def _snapshot_name(source_id: str, original: Path) -> str:
suffix = original.suffix.lower()[:12]
stem = SAFE_FILENAME_RE.sub("-", source_id).strip("-._") or "source"
return f"{stem}{suffix}"
def register_source(
*,
output_dir: Path,
run_id: str,
source_id: str,
source_type: str,
title: str,
official_url: str,
publisher: str,
territorial_applicability: str,
authorization_basis: str,
authorization_reference: str,
selected_by: str,
updated_date: str | None = None,
snapshot: Path | None = None,
) -> dict[str, Any]:
"""Register one selected official source with provenance and optional local bytes."""
run_id = safe_identifier(run_id, field="run_id")
source_id = safe_identifier(source_id, field="source_id")
if source_type not in ALLOWED_SOURCE_TYPES:
raise ValueError(f"unsupported source_type: {source_type}")
title = " ".join(str(title or "").split())
publisher = " ".join(str(publisher or "").split())
territory = " ".join(str(territorial_applicability or "").split())
selected_by = " ".join(str(selected_by or "").split())
if not title or len(title) > 400:
raise ValueError("title must contain 1-400 characters")
if not publisher or len(publisher) > 200:
raise ValueError("publisher must contain 1-200 characters")
if not territory or len(territory) > 200:
raise ValueError("territorial_applicability must contain 1-200 characters")
if not selected_by or len(selected_by) > 120:
raise ValueError("selected_by must identify the human/model selection role")
if authorization_basis not in ALLOWED_AUTHORIZATION_BASES:
raise ValueError("unsupported authorization_basis")
authorization_reference = " ".join(str(authorization_reference or "").split())
if len(authorization_reference) < 3 or len(authorization_reference) > 200:
raise ValueError("authorization_reference must contain 3-200 characters")
official_url = validate_official_source_url(official_url)
normalized_updated_date = (
validate_iso_date(updated_date, field="updated_date") if updated_date else None
)
safe_output = ensure_safe_output_dir(output_dir, plugin_root=PLUGIN_ROOT)
artifact_path: str | None = None
artifact_sha256: str | None = None
if snapshot is not None:
if snapshot.is_symlink() or not snapshot.is_file():
raise ValueError("snapshot must be a regular local file")
if authorization_basis == "browser_assisted_metadata":
raise ValueError(
"browser_assisted_metadata cannot persist content; use metadata only"
)
raw = snapshot.read_bytes()
if len(raw) > MAX_SNAPSHOT_BYTES:
raise ValueError(f"snapshot exceeds {MAX_SNAPSHOT_BYTES} bytes")
destination = safe_output / "sources" / _snapshot_name(source_id, snapshot)
write_private_bytes(destination, raw)
artifact_path = destination.relative_to(safe_output).as_posix()
artifact_sha256 = sha256_bytes(raw)
source = {
"source_id": source_id,
"source_type": source_type,
"title": title,
"publisher": publisher,
"official_url": official_url,
"territorial_applicability": territory,
"updated_date": normalized_updated_date,
"registered_at": iso_now(),
"selection_status": "selected_requires_professional_applicability_review",
"selected_by": selected_by,
"authorization_basis": authorization_basis,
"authorization_reference": authorization_reference,
"artifact_path": artifact_path,
"artifact_sha256": artifact_sha256,
}
manifest = _manifest(safe_output, run_id=run_id)
sources = manifest.get("sources")
if not isinstance(sources, list):
raise ValueError("official source manifest has invalid sources")
sources[:] = [item for item in sources if item.get("source_id") != source_id]
sources.append(source)
sources.sort(key=lambda item: str(item.get("source_id") or ""))
manifest["source_count"] = len(sources)
manifest["updated_at"] = iso_now()
write_private_json(safe_output / "official_sources.json", manifest)
_record_source_registration(
safe_output,
run_id=run_id,
source=source,
)
return source
def main(argv: list[str] | None = None) -> int:
"""Register one official source selected through a public browser or local copy."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--client-engagement", type=Path, required=True)
parser.add_argument("--source-id", required=True)
parser.add_argument(
"--source-type", choices=sorted(ALLOWED_SOURCE_TYPES), required=True
)
parser.add_argument("--title", required=True)
parser.add_argument("--official-url", required=True)
parser.add_argument("--publisher", required=True)
parser.add_argument("--territorial-applicability", required=True)
parser.add_argument(
"--authorization-basis",
choices=sorted(ALLOWED_AUTHORIZATION_BASES),
required=True,
)
parser.add_argument("--authorization-reference", required=True)
parser.add_argument("--selected-by", required=True)
parser.add_argument("--updated-date")
parser.add_argument("--snapshot", type=Path)
args = parser.parse_args(argv)
try:
context = load_running_case_context(
args.client_engagement,
input_paths=[args.snapshot] if args.snapshot is not None else [],
output_dir=args.output_dir,
)
for name in ("run_intake.json", "official_sources.json"):
path = args.output_dir / name
if path.exists():
require_case_artifact_run(path, run_id=context["run_id"])
call_args = vars(args).copy()
call_args.pop("client_engagement")
source = register_source(run_id=context["run_id"], **call_args)
except (AssuranceContractError, OSError, ValueError) as exc:
LOGGER.error("SOURCE_REGISTRATION_BLOCKED: %s", exc)
return 2
LOGGER.info("Registered official source %s", source["source_id"])
return 0
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, format="%(message)s")
raise SystemExit(main())
SHA-256: c1d6ad1aaf686f542469625cc1f129e29c01c6e19fafa0833743a17a5edb34b6