fix: recognize corroborated empty Oracle associations

This commit is contained in:
Wyndham ARR committed 2026-10-08 18:58:18 +08:00
1 parent 4559d5a642
commit de9eb532f2
9 files changed
+735 -28

No files matched your search

+141 -10
View File
@@ -28,6 +28,8 @@ from integrations.ohip.collect_arr_source import json_bytes, require, strict_jso
from integrations.ohip.collect_arr_source import CollectionError
VERSION = "arr-source-field-review/v1"
REANALYSIS_POLICY = "oracle-optional-association/v1"
REANALYSIS_VERSION = "arr-source-reanalysis/v1"
LIMIT = 100 * 1024 * 1024
OPTIONAL = frozenset({"BLOCK_CODE", "RES_COMMENT", "PRODUCTS", "ROOM_CATEGORY_LABEL"})
LABELS = {
@@ -128,6 +130,12 @@ class DataFieldReviews:
state = strict_json(state_raw)
require(state["identity_sha256"] == fingerprint(meta) and type(state["revision"]) is int
and state["status"] in {"editing", "finalized"}, "data_review_state_context_changed")
if "source_reanalysis" in state:
reference = state["source_reanalysis"]
require(type(reference) is dict and reference.get("parent_original_sha256") == meta["original_sha256"]
and reference.get("source_manifest_sha256") == meta["source_manifest_sha256"],
"data_review_reanalysis_context_changed")
self._reanalyzed_source(directory, original, state)
return meta, original, state
def _publish(self, directory, state):
@@ -148,8 +156,74 @@ class DataFieldReviews:
value = [value]
return {"state": "available", "value": value}
def _derive(self, original, state):
data = copy.deepcopy(original)
@staticmethod
def _reanalysis_projection(original, reanalyzed, policy_id):
require(policy_id == REANALYSIS_POLICY and type(reanalyzed) is dict,
"data_review_reanalysis_policy_invalid")
expected = copy.deepcopy(original)
require(type(reanalyzed.get("records")) is list
and len(reanalyzed["records"]) == len(original["records"]),
"data_review_reanalysis_records_changed")
changes = []
allowed = {"BLOCK_CODE": ("missing_reservation_block", ""),
"PRODUCTS": ("missing_reservation_packages", [])}
for index, (old, new) in enumerate(zip(original["records"], reanalyzed["records"])):
require(type(new) is dict and type(new.get("fields")) is dict,
"data_review_reanalysis_record_invalid")
for field, (reason, empty_value) in allowed.items():
before, after = old["fields"][field], new["fields"].get(field)
if fingerprint(before) == fingerprint(after):
continue
empty = {"state": "empty", "value": empty_value}
require(before.get("state") == "missing" and before.get("reason") == reason
and fingerprint(after) == fingerprint(empty),
"data_review_reanalysis_observation_invalid")
expected["records"][index]["fields"][field] = copy.deepcopy(empty)
changes.append({"item_id": f"{old['source_sequence']}:{field}",
"source_sequence": old["source_sequence"], "field": field,
"original_observation": copy.deepcopy(before), "reanalyzed_observation": copy.deepcopy(empty)})
require(bool(changes), "data_review_reanalysis_no_changes")
unresolved = any(observation.get("state") != "available"
and not (field in OPTIONAL and observation.get("state") == "empty")
for row in expected["records"] for field, observation in row["fields"].items())
expected["input_complete"] = not unresolved
expected["status"] = "collected_with_gaps" if unresolved else "collected"
expected["optional_omission_policy"] = REANALYSIS_POLICY
require(fingerprint(expected) == fingerprint(reanalyzed), "data_review_reanalysis_source_changed")
return changes
def _reanalyzed_source(self, directory, original, state):
reference = state.get("source_reanalysis")
if reference is None:
return original, None
require(type(reference) is dict and reference.get("policy_id") == REANALYSIS_POLICY,
"data_review_reanalysis_reference_invalid")
digest, receipt_digest = reference.get("sha256", ""), reference.get("receipt_sha256", "")
manifest_digest = reference.get("manifest_sha256", "")
require(type(digest) is str and bool(re.fullmatch(r"[0-9a-f]{64}", digest))
and type(receipt_digest) is str and bool(re.fullmatch(r"[0-9a-f]{64}", receipt_digest))
and type(manifest_digest) is str and bool(re.fullmatch(r"[0-9a-f]{64}", manifest_digest))
and reference.get("file") == f"reanalyzed-source-{digest}.json"
and reference.get("receipt_file") == f"source-reanalysis-{receipt_digest}.json",
"data_review_reanalysis_reference_invalid")
payload = protected_read(directory / reference["file"], LIMIT)
receipt_raw = protected_read(directory / reference["receipt_file"], LIMIT)
require(_hash(payload) == digest and _hash(receipt_raw) == receipt_digest,
"data_review_reanalysis_changed")
reanalyzed, receipt = strict_json(payload), strict_json(receipt_raw)
changes = self._reanalysis_projection(original, reanalyzed, REANALYSIS_POLICY)
expected_receipt = {"version": REANALYSIS_VERSION, "policy_id": REANALYSIS_POLICY,
"request_id": directory.name, "report_date": original["report_date"], "context": self.context,
"parent_original_sha256": reference["parent_original_sha256"],
"source_manifest_sha256": reference["source_manifest_sha256"],
"reanalyzed_sha256": digest, "reanalysis_manifest_sha256": reference["manifest_sha256"],
"changes": changes}
require(fingerprint(receipt) == fingerprint(expected_receipt), "data_review_reanalysis_receipt_changed")
return reanalyzed, receipt
def _derive(self, directory, original, state):
source, _receipt = self._reanalyzed_source(directory, original, state)
data = copy.deepcopy(source)
for item_id, decision in state["decisions"].items():
sequence, field = item_id.split(":")
row = data["records"][int(sequence) - 1]
@@ -197,7 +271,7 @@ class DataFieldReviews:
return value if observation.get("state") == "available" and isinstance(value, (str, int)) else ""
def _public(self, directory, meta, original, state):
data = self._derive(original, state)
data = self._derive(directory, original, state)
issues = self._issues(directory, data)
keys = set(issues) | set(state["decisions"])
items = []
@@ -209,6 +283,7 @@ class DataFieldReviews:
"field_label": LABELS[field], "confirmation_no": self._display(row, "CONFIRMATION_NO"),
"room_no": self._display(row, "DISP_ROOM_NO"), "company_name": self._display(row, "COMPANY_NAME"),
"rate_code": self._display(row, "RATE_CODE"), "reason_code": issues.get(item_id, "confirmed"),
"source_state": row["fields"][field].get("state"),
"can_be_empty": field in OPTIONAL, "value": decision["value"] if decision else None,
"confirmed": decision is not None and item_id not in issues})
return {"request_id": meta["request_id"], "report_date": meta["report_date"],
@@ -245,13 +320,66 @@ class DataFieldReviews:
require(fingerprint(actual) == fingerprint(meta), "data_review_prepare_conflict")
review = self._public(directory, actual, original, state)
# A complete ordinary source needs no human step or derived source.
return review if review["total_count"] else None
# A reanalysis remains an explicit review until finalized, even if
# its validated source interpretation resolves every listed gap.
return review if review["total_count"] or "source_reanalysis" in state else None
def get(self, request_id):
directory = self._directory(request_id)
with self._lock(directory):
return self._public(directory, *self._read(directory))
def apply_source_reanalysis(self, request_id, payload, manifest_sha256, policy_id, *, expected_revision):
"""Attach a validated offline reinterpretation, without changing acquisition or staff decisions.
This internal maintenance operation has no HTTP route. Its caller must
establish the policy's omission evidence from the saved complete capture.
The projection here prevents any other source or business-value edits.
"""
require(policy_id == REANALYSIS_POLICY, "data_review_reanalysis_policy_invalid")
require(type(payload) is bytes and len(payload) <= LIMIT,
"data_review_reanalysis_payload_invalid")
require(type(manifest_sha256) is str and bool(re.fullmatch(r"[0-9a-f]{64}", manifest_sha256)),
"data_review_reanalysis_manifest_invalid")
reanalyzed = strict_json(payload)
directory = self._directory(request_id)
with self._lock(directory):
meta, original, state = self._read(directory)
if state["status"] != "editing" or (directory / "finalize-intent.json").exists():
raise _error("FROZEN", "已确认生成,来源解释不能再修改", 409)
if type(expected_revision) is not int:
self._revision(state, expected_revision)
digest = _hash(payload)
prior = state.get("source_reanalysis")
if prior is not None:
if prior["sha256"] == digest and prior["manifest_sha256"] == manifest_sha256:
return self._public(directory, meta, original, state)
raise _error("CONFLICT", "该来源已完成重新解释,请核对现有记录", 409)
self._revision(state, expected_revision)
changes = self._reanalysis_projection(original, reanalyzed, policy_id)
receipt = {"version": REANALYSIS_VERSION, "policy_id": policy_id,
"request_id": request_id, "report_date": meta["report_date"], "context": self.context,
"parent_original_sha256": meta["original_sha256"],
"source_manifest_sha256": meta["source_manifest_sha256"], "reanalyzed_sha256": digest,
"reanalysis_manifest_sha256": manifest_sha256, "changes": changes}
receipt_raw = json_bytes(receipt)
receipt_digest = _hash(receipt_raw)
reference = {"policy_id": policy_id, "file": f"reanalyzed-source-{digest}.json", "sha256": digest,
"receipt_file": f"source-reanalysis-{receipt_digest}.json", "receipt_sha256": receipt_digest,
"parent_original_sha256": meta["original_sha256"],
"source_manifest_sha256": meta["source_manifest_sha256"], "manifest_sha256": manifest_sha256}
_write_once(directory / reference["file"], payload)
_write_once(directory / reference["receipt_file"], receipt_raw)
state["source_reanalysis"] = reference
state["revision"] += 1
state["events"].append({"revision": state["revision"], "action": "source_reanalysis",
"policy_id": policy_id, "parent_original_sha256": meta["original_sha256"],
"reanalyzed_sha256": digest, "reanalysis_manifest_sha256": manifest_sha256,
"receipt_sha256": receipt_digest, "at": datetime.now(timezone.utc).isoformat()})
result = self._public(directory, meta, original, state)
self._publish(directory, state)
return result
@staticmethod
def _revision(state, revision):
if type(revision) is not int or revision != state["revision"]:
@@ -312,7 +440,7 @@ class DataFieldReviews:
raise _error("FROZEN", "已确认生成,字段不能再修改", 409)
self._revision(state, revision)
actor = self._actor(actor)
data = self._derive(original, state)
data = self._derive(directory, original, state)
if item_id not in self._issues(directory, data) and item_id not in state["decisions"]:
raise _error(message="只能完善当前任务列出的异常字段")
sequence, field = item_id.split(":")
@@ -328,7 +456,7 @@ class DataFieldReviews:
self._publish(directory, state)
return result
def _frozen(self, meta, original, state):
def _frozen(self, directory, meta, original, state):
changes = []
for item_id, decision in sorted(state["decisions"].items()):
sequence, field = item_id.split(":")
@@ -339,7 +467,10 @@ class DataFieldReviews:
"context": self.context, "original_sha256": meta["original_sha256"],
"source_manifest_sha256": meta["source_manifest_sha256"], "revision": state["revision"],
"changes": changes, "events": state["events"]}
data = self._derive(original, state)
_source, receipt = self._reanalyzed_source(directory, original, state)
if receipt is not None:
audit["source_reanalysis"] = {**receipt, "receipt_sha256": state["source_reanalysis"]["receipt_sha256"]}
data = self._derive(directory, original, state)
data["manual_data_review"] = {"manifest": audit, "manifest_sha256": fingerprint(audit)}
binding = fingerprint({"source_manifest_sha256": meta["source_manifest_sha256"],
"review_manifest_sha256": fingerprint(audit), "data_sha256": _hash(json_bytes(data))})
@@ -354,7 +485,7 @@ class DataFieldReviews:
return self._public(directory, meta, original, state)
self._revision(state, revision)
actor = self._actor(actor)
if self._issues(directory, self._derive(original, state)):
if self._issues(directory, self._derive(directory, original, state)):
raise _error("INCOMPLETE", "请先完善并确认所有异常字段", 409)
intent = directory / "finalize-intent.json"
prior_sha256 = fingerprint(state)
@@ -368,14 +499,14 @@ class DataFieldReviews:
state["events"].append({"revision": state["revision"], "action": "finalize", "actor": actor,
"at": datetime.now(timezone.utc).isoformat()})
atomic_json(intent, {"prior_sha256": prior_sha256, "state": state}, replace=False)
payload, binding = self._frozen(meta, original, state)
payload, binding = self._frozen(directory, meta, original, state)
_write_once(directory / "reviewed-source.json", payload)
state["derived_sha256"], state["binding_manifest_sha256"] = _hash(payload), binding
self._publish(directory, state)
return self._public(directory, meta, original, state)
def _verify_frozen(self, directory, meta, original, state):
payload, binding = self._frozen(meta, original, state)
payload, binding = self._frozen(directory, meta, original, state)
require(_hash(payload) == state["derived_sha256"] and binding == state["binding_manifest_sha256"]
and protected_read(directory / "reviewed-source.json", LIMIT) == payload,
"data_review_frozen_source_changed")