feat: add public h5 dashboard and publish traceability

This commit is contained in:
Wyndham ARR
2026-08-03 13:04:41 +08:00
parent 7e7470821b
commit 2107f00e32
29 changed files with 1047 additions and 71 deletions

View File

@@ -237,6 +237,95 @@ class AtomicReportPublisher:
"an artifact path escaped the controlled project root",
) from None
@staticmethod
def _is_regular_file(path: Path) -> bool:
return path.is_file() and not path.is_symlink()
@staticmethod
def _is_sha256(value: Any) -> bool:
return (
isinstance(value, str)
and len(value) == 64
and all(character in "0123456789abcdef" for character in value)
)
def _existing_publication_metadata(
self,
report: CompanyReport,
reservation: ReservedReport,
built: BuiltWorkbook,
archive_path: Path,
result_path: Path,
) -> Optional[Tuple[FileMetadata, FileMetadata]]:
archive_present = archive_path.exists() or archive_path.is_symlink()
result_present = result_path.exists() or result_path.is_symlink()
if not archive_present and not result_present:
return None
if not self._is_regular_file(archive_path) or not self._is_regular_file(
result_path
):
raise PublicationError(
ErrorCode.PUBLISH_FAILED,
"an existing company report publication is incomplete",
)
semantic_sha256 = built.summary.get("semantic_sha256")
if not self._is_sha256(semantic_sha256):
raise PublicationError(
ErrorCode.PUBLISH_FAILED,
"the generated company report semantic identity is invalid",
)
try:
result_payload = json.loads(result_path.read_text(encoding="utf-8"))
if not isinstance(result_payload, dict):
raise ValueError("result JSON must be an object")
artifact_payload = result_payload.get("artifact")
if not isinstance(artifact_payload, dict):
raise ValueError("result JSON artifact identity is missing")
existing_sha256 = artifact_payload.get("sha256")
if not self._is_sha256(existing_sha256):
raise ValueError("result JSON artifact hash is invalid")
archive_sha256 = sha256_file(archive_path)
if archive_sha256 != existing_sha256:
raise ValueError("archive and result JSON hashes differ")
artifact_storage_key = self._storage_key(archive_path)
expected_payload = self._success_result(
report,
reservation,
artifact_storage_key,
existing_sha256,
semantic_sha256,
)
if result_payload != expected_payload:
raise ValueError("result JSON identity differs")
artifact = FileMetadata(
file_kind="company_ten_day_xlsx",
original_filename=report.filename,
storage_key=artifact_storage_key,
sha256=existing_sha256,
byte_size=archive_path.stat().st_size,
mime_type=XLSX_MIME,
)
result_json = FileMetadata(
file_kind="result_json",
original_filename=result_path.name,
storage_key=self._storage_key(result_path),
sha256=sha256_file(result_path),
byte_size=result_path.stat().st_size,
mime_type="application/json",
)
artifact.validate()
result_json.validate()
return artifact, result_json
except (OSError, UnicodeError, TypeError, ValueError, RepositoryError):
raise PublicationError(
ErrorCode.PUBLISH_FAILED,
"an existing company report publication identity is invalid",
) from None
def _archive_untracked_current(self, current_path: Path, archive_root: Path) -> None:
if not current_path.is_file():
return
@@ -311,6 +400,28 @@ class AtomicReportPublisher:
try:
month_root.mkdir(parents=True, exist_ok=True, mode=0o700)
existing = self._existing_publication_metadata(
report,
reservation,
built,
archive_path,
result_path,
)
if existing is not None:
artifact, result_json = existing
if not current_existed:
self._atomic_copy(archive_path, current_path)
current_replaced = True
repository.activate_report(reservation, artifact, result_json)
return PublicationOutcome(
current_path=current_path,
archive_path=archive_path,
result_path=result_path,
artifact=artifact,
result_json=result_json,
)
self._archive_untracked_current(current_path, archive_root)
if current_existed:
shutil.copyfile(current_path, backup_path)