Copy the full-data packet once per report, not once per domain (issue #122, BUG-1471)
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:
co-authored by
Claude Opus 5.5
parent
f8fa4d66ef
commit
3eeb898071
@@ -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):
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user