Fix Review 64 valuation authority regressions
This commit is contained in:
@@ -259,6 +259,16 @@ def _manual_asset_valuation_items(conn: sqlite3.Connection, active_only: bool =
|
||||
return included, excluded
|
||||
|
||||
|
||||
def _persisted_evidence_array(value: Any) -> list[dict[str, Any]] | None:
|
||||
try:
|
||||
decoded = json.loads(value or "[]")
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
return None
|
||||
if not isinstance(decoded, list) or any(not isinstance(item, dict) for item in decoded):
|
||||
return None
|
||||
return decoded
|
||||
|
||||
|
||||
def _snapshot_rows_for_run(conn: sqlite3.Connection, run_id: str) -> tuple[list[sqlite3.Row], dict[str, Any]]:
|
||||
"""Load one immutable close run and only the rows persisted by that run."""
|
||||
run = conn.execute(
|
||||
@@ -287,6 +297,11 @@ def _snapshot_rows_for_run(conn: sqlite3.Connection, run_id: str) -> tuple[list[
|
||||
)""",
|
||||
(basis_date, run["_run_rowid"]),
|
||||
).fetchone()[0]
|
||||
evidence = {
|
||||
field: _persisted_evidence_array(run[field])
|
||||
for field in ("applied_evidence", "excluded_evidence", "carried_evidence")
|
||||
}
|
||||
integrity_error = any(value is None for value in evidence.values())
|
||||
metadata = {
|
||||
"snapshot_run_id": run["id"],
|
||||
"is_correction": bool(run["correction_of_run_id"]),
|
||||
@@ -298,9 +313,10 @@ def _snapshot_rows_for_run(conn: sqlite3.Connection, run_id: str) -> tuple[list[
|
||||
"approved_at": run["approved_at"],
|
||||
"approval_reason": run["approval_reason"],
|
||||
"approval_reference": run["approval_reference"],
|
||||
"applied_evidence": json.loads(run["applied_evidence"] or "[]"),
|
||||
"excluded_evidence": json.loads(run["excluded_evidence"] or "[]"),
|
||||
"carried_evidence": json.loads(run["carried_evidence"] or "[]"),
|
||||
"applied_evidence": evidence["applied_evidence"] or [],
|
||||
"excluded_evidence": evidence["excluded_evidence"] or [],
|
||||
"carried_evidence": evidence["carried_evidence"] or [],
|
||||
"evidence_integrity_error": integrity_error,
|
||||
"closed_at": run["created_at"] or "",
|
||||
"previous_snapshot_row_count": previous_rows,
|
||||
"total_snapshot_rows_for_basis_date": total_rows,
|
||||
@@ -372,7 +388,10 @@ def _closed_run_metadata(metadata: dict[str, Any]) -> dict[str, Any]:
|
||||
for item in exclusions:
|
||||
if item.get("candidate"):
|
||||
key = (item.get("source_type", ""), item.get("source_id", ""))
|
||||
warning = candidate_warnings.setdefault(key, {"code": "unverified_candidate", "source_type": key[0], "source_id": key[1], "candidates": []})
|
||||
warning_shape = {"code": "unverified_candidate", "source_type": key[0], "source_id": key[1], "candidates": []}
|
||||
if item.get("blocking") is False:
|
||||
warning_shape["blocking"] = False
|
||||
warning = candidate_warnings.setdefault(key, warning_shape)
|
||||
warning["candidates"].append(item["candidate"])
|
||||
else:
|
||||
warnings.append({"code": item.get("reason", "excluded_item"), "source_type": item.get("source_type", ""), "source_id": item.get("source_id", ""), "currency": item.get("currency", "KRW")})
|
||||
@@ -667,6 +686,8 @@ def close_asset_snapshot(basis_date: str = "", source_note: str = "", memo: str
|
||||
if preview_fingerprint and preview_fingerprint != _preview_fingerprint(preview, basis, selection_policy):
|
||||
return fail("authority_drift", "asset authority changed since preview", basis_date=basis)
|
||||
latest_rows, latest_meta = _snapshot_rows_for_latest_run(conn, basis)
|
||||
if latest_meta.get("evidence_integrity_error"):
|
||||
return fail("data_integrity_error", "snapshot evidence is malformed", snapshot_run_id=latest_meta.get("snapshot_run_id", ""))
|
||||
legacy_snapshot_rows = bool(latest_rows and not latest_meta.get("snapshot_run_id"))
|
||||
if legacy_snapshot_rows and not correction_reason.strip():
|
||||
return fail(
|
||||
@@ -734,6 +755,8 @@ def close_asset_snapshot(basis_date: str = "", source_note: str = "", memo: str
|
||||
or immutable_replay_run
|
||||
):
|
||||
replay_rows, replay_meta = _snapshot_rows_for_run(conn, existing_run["id"])
|
||||
if replay_meta.get("evidence_integrity_error"):
|
||||
return fail("data_integrity_error", "snapshot evidence is malformed", snapshot_run_id=existing_run["id"])
|
||||
replay_items = [_public_evidence(item) for item in replay_meta.get("applied_evidence", [])]
|
||||
replay_warnings = _closed_warnings(replay_meta, basis, replay_rows)
|
||||
replay_public = _closed_run_metadata(replay_meta)
|
||||
@@ -816,7 +839,7 @@ def close_asset_snapshot(basis_date: str = "", source_note: str = "", memo: str
|
||||
),
|
||||
)
|
||||
snapshot_ids.append(snapshot_id)
|
||||
applied_evidence = [{k: item.get(k) for k in ("id", "source_type", "source_id", "authority_source", "name", "type", "liquidity_bucket", "active", "amount", "valuation_amount", "last_balance", "last_synced_at", "currency", "valuation_id", "valuation_kind", "valuation_date", "age_days", "carried_forward", "verification_status", "evidence_kind") if item.get(k) is not None} for item in included]
|
||||
applied_evidence = [{k: item.get(k) for k in ("id", "source_type", "source_id", "authority_source", "name", "type", "liquidity_bucket", "active", "amount", "valuation_amount", "last_balance", "last_synced_at", "currency", "valuation_id", "valuation_kind", "valuation_date", "age_days", "carried_forward", "verification_status", "evidence_kind", "unverified_candidates") if item.get(k) is not None} for item in included]
|
||||
excluded_evidence = [{k: item.get(k) for k in ("source_type", "source_id", "reason", "currency")} for item in preview.get("excluded_items", [])]
|
||||
excluded_evidence.extend(
|
||||
{
|
||||
@@ -824,6 +847,7 @@ def close_asset_snapshot(basis_date: str = "", source_note: str = "", memo: str
|
||||
"source_id": warning.get("source_id", ""),
|
||||
"reason": "unverified_candidate",
|
||||
"candidate": candidate,
|
||||
"blocking": warning.get("blocking", True),
|
||||
"state_hash": next(
|
||||
state["state_hash"]
|
||||
for state in preview["_candidate_state_hashes"]
|
||||
@@ -922,6 +946,8 @@ def get_asset_snapshot_report(basis_date: str = "", db_path: str | None = None,
|
||||
return fail("validation_error", f"invalid date: {basis_date}")
|
||||
with read_db(db_path) as conn:
|
||||
rows, close_metadata = _snapshot_rows_for_latest_run(conn, basis)
|
||||
if close_metadata.get("evidence_integrity_error"):
|
||||
return fail("data_integrity_error", "snapshot evidence is malformed", snapshot_run_id=close_metadata.get("snapshot_run_id", ""))
|
||||
has_close_run = bool(close_metadata.get("snapshot_run_id"))
|
||||
snapshots = _snapshot_items(conn, basis) if rows else []
|
||||
legacy_snapshots = snapshots if rows and not has_close_run else []
|
||||
@@ -1032,8 +1058,10 @@ def get_net_worth_timeseries(start_date: str = "", end_date: str = "", months: i
|
||||
points = []
|
||||
for row in basis_rows:
|
||||
basis = row["basis_date"]
|
||||
items = _snapshot_items(conn, basis)
|
||||
_, metadata = _snapshot_rows_for_latest_run(conn, basis)
|
||||
if metadata.get("evidence_integrity_error"):
|
||||
return fail("data_integrity_error", "snapshot evidence is malformed", snapshot_run_id=metadata.get("snapshot_run_id", ""))
|
||||
items = _snapshot_items(conn, basis)
|
||||
run_public = _closed_run_metadata(metadata)
|
||||
run_public["warnings"] = _closed_warnings(metadata, basis, conn.execute("SELECT rowid AS _rowid, * FROM asset_snapshots WHERE basis_date=? ORDER BY created_at ASC, name ASC", (basis,)).fetchall())
|
||||
run_provenance = _evidence_provenance(metadata.get("applied_evidence", []), basis)
|
||||
|
||||
Reference in New Issue
Block a user