Source code for sim_panel.products.io
from __future__ import annotations
import json
from pathlib import Path
from typing import Dict, Iterable, List
from .records import ProductRecord
from sim_panel.io.jsonl import read_jsonl_dicts, write_jsonl_dicts
def read_jsonl(path: str | Path) -> List[Dict]:
p = Path(path)
if not p.exists():
return []
return read_jsonl_dicts(str(p))
def write_jsonl_atomic(path: str | Path, rows: Iterable[Dict]) -> None:
p = Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
# Delegate to shared JSONL writer (atomic write with fsync) for robustness.
write_jsonl_dicts(str(p), rows)
[docs]
def load_product_records(path: str | Path) -> List[ProductRecord]:
return [ProductRecord.from_dict(r) for r in read_jsonl(path)]
[docs]
def save_product_records(path: str | Path, records: Iterable[ProductRecord]) -> None:
recs = sorted(records, key=lambda r: (r.product_id, r.display_variant))
write_jsonl_atomic(path, (r.to_dict() for r in recs))
[docs]
def merge_product_records(
base: List[ProductRecord],
incoming: List[ProductRecord],
*,
prefer_incoming_attributes: bool = True,
prefer_incoming_display: bool = False,
) -> List[ProductRecord]:
"""
Merge by (product_id, display_variant).
- attributes: prefer incoming by default
- display fields: prefer base by default unless prefer_incoming_display=True
"""
index: Dict[tuple[str, str], ProductRecord] = {}
for r in base:
index[(r.product_id, r.display_variant)] = r
for r in incoming:
key = (r.product_id, r.display_variant)
if key not in index:
index[key] = r
continue
existing = index[key]
if prefer_incoming_attributes:
existing.attributes = r.attributes or existing.attributes
existing.spec_key = None # recompute
if prefer_incoming_display:
if r.display_name is not None:
existing.display_name = r.display_name
if r.display_text is not None:
existing.display_text = r.display_text
existing.text_key = None
if r.meta:
existing.meta = {**existing.meta, **r.meta}
if r.provenance:
existing.provenance = {**existing.provenance, **r.provenance}
out = list(index.values())
for r in out:
r.compute_keys()
return out