Copy the full-data packet once per report, not once per domain (issue #122, BUG-1471)
Independent Staging Quality Gate / validate (push) Successful in 15m27s
Independent Staging Quality Gate / publish (push) Successful in 3m43s

read_full_data_domain deep-copied the shared worksheets on every call; a long
report reads five domains twice, so ten copies for read-only use. The shared
part is now copied once (full_data_domain_base) and reused for every domain's
facts and their leaf check; each domain's facts are still copied out so domains
share no objects. Long reports byte-identical on four fictional charts; with
#121, -24–29% end to end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoz1Qk3y9buDv1mkmTu
This commit is contained in:
Jesse_Chen
2026-10-28 07:06:32 +08:00
co-authored by Claude Opus 5.5
parent f8fa4d66ef
commit 3eeb898071
6 changed files with 99 additions and 17 deletions
+20 -7
View File
@@ -8,10 +8,12 @@ judgment table; the card builder does that from these leaves.
from __future__ import annotations
import copy
import json
from collections.abc import Mapping
from typing import Any
from pl9_full_data_export import read_full_data_domain
from pl9_full_data_export import full_data_domain_base, read_full_data_domain
PRECISION_SENTENCE = "出生时间精度不够,不参与"
RAHU_NOTE = "罗计不单独定功能吉凶"
@@ -783,10 +785,16 @@ def _factors(domain: str, timing: dict[str, Any], karakas: dict[str, dict[str, A
return factors
def build_domain_facts(packet: dict, domain: str, *, minute_confirmed: bool) -> dict[str, Any]:
def build_domain_facts(
packet: dict,
domain: str,
*,
minute_confirmed: bool,
base: Mapping | None = None,
) -> dict[str, Any]:
if domain not in PACKET_DOMAIN:
raise KeyError(domain)
view = read_full_data_domain(packet, PACKET_DOMAIN[domain])
view = read_full_data_domain(packet, PACKET_DOMAIN[domain], base=base)
varga = {
"career": "D10", "wealth": "D2", "marriage": "D9", "health": "D6",
"children": "D7", "parents": "D12", "education": "D24", "migration": "D4",
@@ -824,7 +832,9 @@ def build_domain_facts(packet: dict, domain: str, *, minute_confirmed: bool) ->
encoded = json.dumps(facts, ensure_ascii=False, default=str)
if "tajika_named_yogas" in encoded:
raise RuntimeError("consult card facts must not read tajika_named_yogas")
return facts
# Leaves may be objects of the view. With a shared base, two domains' facts
# would share them; each domain's facts stay their own, as before.
return facts if base is None else copy.deepcopy(facts)
def build_consult_card_facts(
@@ -833,13 +843,16 @@ def build_consult_card_facts(
*,
minute_confirmed: bool | None,
body: dict | None = None,
base: Mapping | None = None,
) -> dict[str, Any]:
confirmed = minute_confirmed_from_body(body) if minute_confirmed is None else minute_confirmed
rows = []
if base is None and any(domain in PACKET_DOMAIN for domain in domains):
base = full_data_domain_base(packet)
for domain in domains:
if domain not in PACKET_DOMAIN:
continue
rows.append(build_domain_facts(packet, domain, minute_confirmed=confirmed))
rows.append(build_domain_facts(packet, domain, minute_confirmed=confirmed, base=base))
return {
"schema": "consult-card-facts-v1",
"minute_confirmed": confirmed,
@@ -848,9 +861,9 @@ def build_consult_card_facts(
}
def assert_fact_leaves(packet: dict, facts: dict[str, Any]) -> None:
def assert_fact_leaves(packet: dict, facts: dict[str, Any], *, base: Mapping | None = None) -> None:
"""Every copied leaf equals the packet. Derived selections are not leaves."""
view = read_full_data_domain(packet, facts["packet_domain"])
view = read_full_data_domain(packet, facts["packet_domain"], base=base)
def check(item: Any) -> None:
if isinstance(item, list):
+23 -8
View File
@@ -2472,19 +2472,18 @@ def _append_annual_method_reference(markdown: str, packet: dict, language: str)
return markdown.rstrip() + "\n\n" + extra + "\n"
def read_full_data_domain(packet: Mapping, domain: str) -> dict:
"""Deep-copied packet view for one consult domain.
def full_data_domain_base(packet: Mapping) -> dict:
"""The detached, domain-independent part of every domain view.
Field selection belongs to the consult card. This entry only freezes the
domain name and detaches the copy from the live packet.
One deep copy of the packet's shared sections. ``read_full_data_domain``
used to make this copy once per domain, and a long report reads five
domains twice (facts, then the leaf check): ten copies of the whole
``worksheets`` tree for read-only use (issue #122). Callers that read
several domains of one packet build this once and pass it as ``base``.
"""
if domain not in FULL_DATA_DOMAINS:
raise KeyError(domain)
if not isinstance(packet, Mapping):
raise TypeError("packet must be a mapping")
copied = copy.deepcopy({
"domain": domain,
"label": FULL_DATA_DOMAINS[domain],
"report_version": packet.get("report_version"),
"birth_info": packet.get("birth_info"),
"core_chart": packet.get("core_chart"),
@@ -2496,3 +2495,19 @@ def read_full_data_domain(packet: Mapping, domain: str) -> dict:
if isinstance(timing, dict):
timing.pop("pyjhora_annual_method_reference", None)
return copied
def read_full_data_domain(packet: Mapping, domain: str, *, base: Mapping | None = None) -> dict:
"""Detached packet view for one consult domain.
Field selection belongs to the consult card. This entry only freezes the
domain name and detaches the view from the live packet. With ``base`` (from
``full_data_domain_base`` of the same packet) the shared sections are that
one copy; views of different domains then share it and must be read only.
"""
if domain not in FULL_DATA_DOMAINS:
raise KeyError(domain)
if not isinstance(packet, Mapping):
raise TypeError("packet must be a mapping")
shared = full_data_domain_base(packet) if base is None else base
return {"domain": domain, "label": FULL_DATA_DOMAINS[domain], **shared}
+6 -2
View File
@@ -14,9 +14,11 @@ from typing import Any
try:
from scripts.consult_card_domain_facts import assert_fact_leaves, build_consult_card_facts, dig
from scripts.consult_full_data_cache import _attach_graha_drishti
from scripts.pl9_full_data_export import full_data_domain_base
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution path
from consult_card_domain_facts import assert_fact_leaves, build_consult_card_facts, dig
from consult_full_data_cache import _attach_graha_drishti
from pl9_full_data_export import full_data_domain_base
READING_FACTS_SCHEMA = "report-reading-facts-v1"
READING_DOMAINS = ("general", "career", "wealth", "marriage", "health")
@@ -87,10 +89,12 @@ def build_report_reading_facts(
) -> dict[str, Any]:
"""Compact, packet-sourced facts for the reading. Raises on any non-packet leaf."""
working = _attach_graha_drishti(copy.deepcopy(packet), full_reading)
raw = build_consult_card_facts(working, list(READING_DOMAINS), minute_confirmed=minute_confirmed)
# One detached copy of the packet serves every domain's facts and their leaf check (issue #122).
base = full_data_domain_base(working)
raw = build_consult_card_facts(working, list(READING_DOMAINS), minute_confirmed=minute_confirmed, base=base)
rows = {row["domain"]: row for row in raw["domains"]}
for row in rows.values():
assert_fact_leaves(working, row)
assert_fact_leaves(working, row, base=base)
general = rows["general"]
planets = {}
for name, record in general["planets"].items():