feat: add raw artifact audit trail

This commit is contained in:
2026-06-24 09:38:15 +08:00
parent 6a17c9a336
commit a770070b34
12 changed files with 670 additions and 16 deletions

View File

@@ -7,6 +7,7 @@ from typing import Optional
from .db import DEFAULT_DB_PATH, connect, import_template_file, init_db, load_sample_data
from .indicators import compute_area_scores
from .import_templates import ImportTemplateError, validate_tabular_file
from .raw_artifacts import fingerprint_file
from .reporting import format_scores_table, write_monthly_report
@@ -69,6 +70,17 @@ def build_parser() -> argparse.ArgumentParser:
help="CSV, XLSX, or XLSM file to import.",
)
fingerprint_parser = subparsers.add_parser(
"fingerprint-file",
help="Compute raw artifact metadata for an import file.",
)
fingerprint_parser.add_argument(
"--file",
type=Path,
required=True,
help="File to hash and describe.",
)
return parser
@@ -132,5 +144,10 @@ def main(argv: Optional[list[str]] = None) -> int:
)
return 0
if args.command == "fingerprint-file":
fingerprint = fingerprint_file(args.file)
print(fingerprint.to_json())
return 0
parser.error(f"Unknown command: {args.command}")
return 2

View File

@@ -0,0 +1,40 @@
from __future__ import annotations
import hashlib
import json
import mimetypes
from dataclasses import asdict, dataclass
from pathlib import Path
@dataclass(frozen=True)
class RawArtifactFingerprint:
original_filename: str
raw_uri: str
sha256: str
size_bytes: int
mime_type: str | None
def to_json(self) -> str:
return json.dumps(asdict(self), ensure_ascii=False, indent=2)
def fingerprint_file(file_path: Path) -> RawArtifactFingerprint:
path = file_path.resolve()
digest = hashlib.sha256()
size_bytes = 0
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
size_bytes += len(chunk)
sha256 = digest.hexdigest()
mime_type, _ = mimetypes.guess_type(path.name)
return RawArtifactFingerprint(
original_filename=path.name,
raw_uri=f"raw://sha256/{sha256}/{path.name}",
sha256=sha256,
size_bytes=size_bytes,
mime_type=mime_type,
)