feat(products): export hosted detail URL for fetched images; snapshot carries status (SD-0002 §6.5.5, INV-16)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ from collections.abc import Iterable, Iterator
|
||||
from decimal import Decimal
|
||||
|
||||
from .diff import CatalogProduct
|
||||
from .hosted import EXPORT_RENDITION, image_url
|
||||
from .models import (
|
||||
IMAGE_COLUMNS,
|
||||
OPTION_VALUE_COLUMNS,
|
||||
@@ -51,14 +52,14 @@ def _cell(value: object) -> str:
|
||||
return str(value)
|
||||
|
||||
|
||||
def catalog_to_csv(products: Iterable[CatalogProduct]) -> Iterator[str]:
|
||||
def catalog_to_csv(products: Iterable[CatalogProduct], base_url: str = "") -> Iterator[str]:
|
||||
"""Stream canonical CSV text, header first, one product block at a time."""
|
||||
buf = io.StringIO()
|
||||
writer = csv.writer(buf)
|
||||
writer.writerow(HEADER)
|
||||
yield _drain(buf)
|
||||
for product in products:
|
||||
for row in _product_rows(product):
|
||||
for row in _product_rows(product, base_url):
|
||||
writer.writerow([row.get(col, "") for col in HEADER])
|
||||
yield _drain(buf)
|
||||
|
||||
@@ -70,7 +71,7 @@ def _drain(buf: io.StringIO) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def _product_rows(product: CatalogProduct) -> list[dict[str, str]]:
|
||||
def _product_rows(product: CatalogProduct, base_url: str = "") -> list[dict[str, str]]:
|
||||
"""The product's CSV rows (§6.5.1 grammar): product fields + option names on
|
||||
the first row; one variant per row; images interleaved; image-only rows when
|
||||
a product has more images than variants."""
|
||||
@@ -90,13 +91,18 @@ def _product_rows(product: CatalogProduct) -> list[dict[str, str]]:
|
||||
if i < len(product.variants):
|
||||
_write_variant(row, product, product.variants[i])
|
||||
if i < len(product.images):
|
||||
_write_image(row, product.images[i])
|
||||
_write_image(row, product.images[i], base_url)
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
|
||||
def _write_image(row: dict[str, str], image) -> None:
|
||||
row["Image Src"] = image.source_url
|
||||
def _write_image(row: dict[str, str], image, base_url: str) -> None:
|
||||
# Fetched images export the platform-hosted URL (INV-12/16); everything
|
||||
# else keeps the original source_url so nothing is lost (§6.5.5).
|
||||
if image.status == "fetched":
|
||||
row["Image Src"] = image_url(base_url, image.id, EXPORT_RENDITION)
|
||||
else:
|
||||
row["Image Src"] = image.source_url
|
||||
row["Image Position"] = str(image.position)
|
||||
if image.alt_text is not None:
|
||||
row["Image Alt Text"] = image.alt_text
|
||||
|
||||
Reference in New Issue
Block a user