235 lines
8.6 KiB
Python
235 lines
8.6 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from .product_package_schema import (
|
|
CANONICAL_PACKAGE_LAYOUT,
|
|
CANONICAL_PACKAGE_SCHEMA,
|
|
build_canonical_descriptor,
|
|
normalize_package_manifest,
|
|
)
|
|
|
|
|
|
def _clean_dict(payload: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
|
return dict(payload or {})
|
|
|
|
|
|
def _kind_from_engine(engine_code: str) -> Optional[str]:
|
|
normalized = str(engine_code or "").strip().lower()
|
|
if normalized in {"envi", "sarscape"}:
|
|
return "windows"
|
|
if normalized in {"isce2", "pyint", "gamma"}:
|
|
return "wsl"
|
|
return None
|
|
|
|
|
|
def _stable_digest(*parts: Any, length: int = 20) -> str:
|
|
payload = "||".join(str(part or "") for part in parts)
|
|
return hashlib.sha1(payload.encode("utf-8", errors="ignore")).hexdigest()[:length]
|
|
|
|
|
|
def build_dinsar_package_manifest(
|
|
*,
|
|
product_id: str,
|
|
display_name: str,
|
|
task_name: Optional[str],
|
|
engine_code: str,
|
|
engine_version: Optional[str],
|
|
processor_code: Optional[str],
|
|
profile_code: Optional[str],
|
|
runtime_id: Optional[str],
|
|
source_primary_path: str,
|
|
source_dir: str,
|
|
publish_dir: str,
|
|
identity: Dict[str, Any],
|
|
source: Dict[str, Any],
|
|
run: Dict[str, Any],
|
|
temporal: Dict[str, Any],
|
|
spatial: Dict[str, Any],
|
|
dinsar_profile: Dict[str, Any],
|
|
pairing_trace: Dict[str, Any],
|
|
labels: Dict[str, Any],
|
|
summary: Dict[str, Any],
|
|
assets: List[Dict[str, Any]],
|
|
issues: Optional[List[Dict[str, Any]]] = None,
|
|
) -> Dict[str, Any]:
|
|
resolved_processor_code = (
|
|
processor_code
|
|
or (f"{engine_code}_{profile_code}" if profile_code else None)
|
|
or engine_code
|
|
)
|
|
document = {
|
|
"schema_version": CANONICAL_PACKAGE_SCHEMA,
|
|
"package_layout": CANONICAL_PACKAGE_LAYOUT,
|
|
"catalog_name": "dinsar",
|
|
"product_family": "dinsar",
|
|
"product_type": "dinsar_interferogram",
|
|
"product_id": product_id,
|
|
"display_name": display_name,
|
|
"task_name": task_name or display_name,
|
|
"pair_key": identity.get("pair_key"),
|
|
"run_key": identity.get("run_key"),
|
|
"identity": {
|
|
"pair_key": identity.get("pair_key"),
|
|
"stack_key": identity.get("stack_key"),
|
|
"run_key": identity.get("run_key"),
|
|
"task_alias": identity.get("task_alias"),
|
|
},
|
|
"engine": {
|
|
"code": engine_code,
|
|
"version": engine_version,
|
|
},
|
|
"processor": {
|
|
"code": resolved_processor_code,
|
|
"profile_code": profile_code,
|
|
},
|
|
"runtime": {
|
|
"runtime_id": runtime_id,
|
|
"kind": _kind_from_engine(engine_code),
|
|
},
|
|
"source": {
|
|
**_clean_dict(source),
|
|
"primary_path": source_primary_path,
|
|
"source_dir": source_dir,
|
|
"publish_dir": publish_dir,
|
|
"native_output_dir": (
|
|
_clean_dict(source).get("native_output_dir")
|
|
or _clean_dict(source).get("output_dir")
|
|
),
|
|
},
|
|
"run": _clean_dict(run),
|
|
"temporal": _clean_dict(temporal),
|
|
"spatial": _clean_dict(spatial),
|
|
"dinsar_profile": _clean_dict(dinsar_profile),
|
|
"labels": _clean_dict(labels),
|
|
"pairing_trace": _clean_dict(pairing_trace),
|
|
"summary": _clean_dict(summary),
|
|
"assets": list(assets or []),
|
|
"canonical": build_canonical_descriptor(assets or [], product_family="dinsar"),
|
|
"issues": list(issues or []),
|
|
"published_at": _clean_dict(temporal).get("published_at"),
|
|
"produced_at": _clean_dict(temporal).get("produced_at"),
|
|
"engine_code": engine_code,
|
|
"processor_code": resolved_processor_code,
|
|
"runtime_id": runtime_id,
|
|
}
|
|
return normalize_package_manifest(document)
|
|
|
|
|
|
def upgrade_timeseries_package_manifest(
|
|
payload: Dict[str, Any],
|
|
*,
|
|
run_context: Dict[str, Any],
|
|
source_summary: Dict[str, Any],
|
|
) -> Dict[str, Any]:
|
|
document = normalize_package_manifest(payload)
|
|
engine_code = str(run_context.get("engine_code") or (document.get("engine") or {}).get("code") or "unknown")
|
|
processor_code = str(
|
|
run_context.get("processor_code")
|
|
or document.get("processor_code")
|
|
or ((document.get("processor") or {}).get("code"))
|
|
or "unknown"
|
|
)
|
|
runtime_id = run_context.get("runtime_id") or document.get("runtime_id")
|
|
|
|
document.update(
|
|
{
|
|
"schema_version": CANONICAL_PACKAGE_SCHEMA,
|
|
"package_layout": CANONICAL_PACKAGE_LAYOUT,
|
|
"catalog_name": "psinsar",
|
|
"product_family": "timeseries",
|
|
"product_type": "timeseries_bundle",
|
|
"run_id": run_context.get("run_id"),
|
|
"run_name": run_context.get("run_name"),
|
|
"batch_id": run_context.get("batch_id"),
|
|
"plan_id": run_context.get("plan_id"),
|
|
"plan_strategy": run_context.get("plan_strategy"),
|
|
"task_id": run_context.get("task_id"),
|
|
"workflow_run_id": run_context.get("workflow_run_id"),
|
|
"mode": run_context.get("mode"),
|
|
"engine_code": engine_code,
|
|
"processor_code": processor_code,
|
|
"runtime_id": runtime_id,
|
|
"stack_key": run_context.get("stack_key") or document.get("stack_key"),
|
|
"group_key": run_context.get("group_key") or document.get("group_key"),
|
|
"reference_date": run_context.get("reference_date") or document.get("reference_date"),
|
|
"stack_dates": run_context.get("stack_dates") or document.get("stack_dates") or [],
|
|
"published_at": run_context.get("published_at") or document.get("published_at"),
|
|
"produced_at": run_context.get("produced_at") or document.get("produced_at"),
|
|
"source_summary": {
|
|
**_clean_dict(document.get("source_summary")),
|
|
**_clean_dict(source_summary),
|
|
},
|
|
}
|
|
)
|
|
if not str(document.get("product_id") or "").strip():
|
|
document["product_id"] = "psinsar_" + _stable_digest(
|
|
run_context.get("run_id"),
|
|
document.get("stack_key") or document.get("group_key"),
|
|
document.get("reference_date"),
|
|
length=20,
|
|
)
|
|
|
|
document["identity"] = {
|
|
**_clean_dict(document.get("identity")),
|
|
"stack_key": document.get("stack_key") or document.get("group_key"),
|
|
"run_key": run_context.get("run_id") or _clean_dict(document.get("identity")).get("run_key"),
|
|
"plan_id": run_context.get("plan_id") or _clean_dict(document.get("identity")).get("plan_id"),
|
|
}
|
|
document["engine"] = {
|
|
**_clean_dict(document.get("engine")),
|
|
"code": engine_code,
|
|
}
|
|
document["processor"] = {
|
|
**_clean_dict(document.get("processor")),
|
|
"code": processor_code,
|
|
"profile_code": processor_code,
|
|
}
|
|
runtime_payload = {
|
|
**_clean_dict(document.get("runtime")),
|
|
**_clean_dict(run_context.get("runtime")),
|
|
}
|
|
runtime_payload["runtime_id"] = runtime_id
|
|
runtime_payload["kind"] = runtime_payload.get("kind") or _kind_from_engine(engine_code)
|
|
document["runtime"] = runtime_payload
|
|
|
|
source_payload = {
|
|
**_clean_dict(document.get("source")),
|
|
"publish_dir": run_context.get("publish_dir"),
|
|
"native_output_dir": (
|
|
run_context.get("native_output_dir")
|
|
or _clean_dict(source_summary).get("mintpy_work_dir_windows")
|
|
or _clean_dict(document.get("source")).get("native_output_dir")
|
|
),
|
|
"work_dir": (
|
|
run_context.get("work_dir")
|
|
or _clean_dict(source_summary).get("generated_stack_manifest_path_windows")
|
|
or _clean_dict(document.get("source")).get("work_dir")
|
|
),
|
|
"source_root": (
|
|
run_context.get("source_root")
|
|
or _clean_dict(source_summary).get("selected_manifest_path_windows")
|
|
or _clean_dict(document.get("source")).get("source_root")
|
|
),
|
|
}
|
|
document["source"] = source_payload
|
|
|
|
temporal_payload = {
|
|
**_clean_dict(document.get("temporal")),
|
|
"reference_date": document.get("reference_date"),
|
|
"stack_dates": [
|
|
str(item).strip()
|
|
for item in document.get("stack_dates") or []
|
|
if str(item).strip()
|
|
],
|
|
"published_at": document.get("published_at"),
|
|
"produced_at": document.get("produced_at"),
|
|
}
|
|
document["temporal"] = temporal_payload
|
|
document["canonical"] = build_canonical_descriptor(
|
|
document.get("assets") or [],
|
|
product_family="timeseries",
|
|
)
|
|
return normalize_package_manifest(document)
|