feat(products): diff resolves hosted image URLs to existing records — INV-12 over images (SD-0002 §6.3)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,10 +21,11 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass, field, replace
|
||||
from decimal import Decimal
|
||||
|
||||
from .models import CLEAR_DEFAULTS, CanonicalProduct, CanonicalVariant
|
||||
from . import hosted
|
||||
from .models import CLEAR_DEFAULTS, CanonicalProduct, CanonicalVariant, RowError
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -119,6 +120,7 @@ def compute_diff(catalog: dict[str, CatalogProduct], products: list[CanonicalPro
|
||||
summary = {"adds": 0, "updates": 0, "unchanged": 0, "errors": 0}
|
||||
for product in products:
|
||||
current = catalog.get(product.handle)
|
||||
_resolve_hosted_images(product, current)
|
||||
if product.errors:
|
||||
product_plan = ProductPlan(kind="error", canonical=product, catalog=current)
|
||||
detail: dict = {"errors": [e.as_json() for e in product.errors]}
|
||||
@@ -145,6 +147,31 @@ def compute_diff(catalog: dict[str, CatalogProduct], products: list[CanonicalPro
|
||||
return DiffResult(records=records, summary=summary, fingerprint=fingerprint, plan=plan)
|
||||
|
||||
|
||||
def _resolve_hosted_images(product: CanonicalProduct, current: CatalogProduct | None) -> None:
|
||||
"""Recognize re-imported hosted image URLs (INV-12). A hosted URL is resolved
|
||||
to this product's existing image with that id — re-keyed onto that image's
|
||||
source_url so the by_src match treats it as the existing image (never an add,
|
||||
never a fetch). A hosted URL whose id is not one of this product's images
|
||||
(cross-storefront, deleted, or a brand-new product) is a row error."""
|
||||
current_by_id = {i.id: i for i in current.images} if current else {}
|
||||
resolved: list = []
|
||||
for image in product.images:
|
||||
hosted_id = hosted.parse_image_id(image.source_url)
|
||||
if hosted_id is None:
|
||||
resolved.append(image)
|
||||
continue
|
||||
match = current_by_id.get(hosted_id)
|
||||
if match is None:
|
||||
product.errors.append(
|
||||
RowError(image.line_number, "Image Src",
|
||||
f"image '{image.source_url}' refers to an unknown hosted id")
|
||||
)
|
||||
resolved.append(image)
|
||||
continue
|
||||
resolved.append(replace(image, source_url=match.source_url))
|
||||
product.images = resolved
|
||||
|
||||
|
||||
def _add_plan(product: CanonicalProduct) -> ProductPlan:
|
||||
return ProductPlan(
|
||||
kind="add",
|
||||
|
||||
Reference in New Issue
Block a user