fix: correct tojson|safe usage in templates and update validator

- Remove |safe from |tojson in HTML attributes (x-data) - quotes must
  become " for browsers to parse correctly
- Update LANG-002 and LANG-003 architecture rules to document correct
  |tojson usage patterns:
  - HTML attributes: |tojson (no |safe)
  - Script blocks: |tojson|safe
- Fix validator to warn when |tojson|safe is used in x-data (breaks
  HTML attribute parsing)
- Improve code quality across services, APIs, and tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-13 22:59:51 +01:00
parent 94d268f330
commit 9920430b9e
123 changed files with 1408 additions and 840 deletions

View File

@@ -20,7 +20,9 @@ from sqlalchemy.orm import Session
from models.database.marketplace_import_job import MarketplaceImportError
from models.database.marketplace_product import MarketplaceProduct
from models.database.marketplace_product_translation import MarketplaceProductTranslation
from models.database.marketplace_product_translation import (
MarketplaceProductTranslation,
)
logger = logging.getLogger(__name__)
@@ -215,9 +217,7 @@ class CSVProcessor:
return processed_data
def _extract_translation_data(
self, product_data: dict[str, Any]
) -> dict[str, Any]:
def _extract_translation_data(self, product_data: dict[str, Any]) -> dict[str, Any]:
"""Extract translation fields from product data.
Returns a dict with title, description, etc. that belong
@@ -363,11 +363,22 @@ class CSVProcessor:
if row_data:
# Keep only key fields for review
limited_data = {
k: v for k, v in row_data.items()
if k in [
"marketplace_product_id", "title", "gtin", "mpn", "sku",
"brand", "price", "availability", "link"
] and v is not None and str(v).strip()
k: v
for k, v in row_data.items()
if k
in [
"marketplace_product_id",
"title",
"gtin",
"mpn",
"sku",
"brand",
"price",
"availability",
"link",
]
and v is not None
and str(v).strip()
}
row_data = limited_data if limited_data else None
@@ -419,7 +430,11 @@ class CSVProcessor:
product_data["vendor_name"] = vendor_name
# Get identifier for error tracking
identifier = product_data.get("marketplace_product_id") or product_data.get("gtin") or product_data.get("mpn")
identifier = (
product_data.get("marketplace_product_id")
or product_data.get("gtin")
or product_data.get("mpn")
)
# Validate required fields
if not product_data.get("marketplace_product_id"):
@@ -524,7 +539,8 @@ class CSVProcessor:
row_number=row_number,
error_type="processing_error",
error_message=str(e),
identifier=row_dict.get("marketplace_product_id") or row_dict.get("id"),
identifier=row_dict.get("marketplace_product_id")
or row_dict.get("id"),
row_data=row_dict,
)
errors += 1

View File

@@ -54,16 +54,18 @@ class GTINProcessor:
# Standard lengths - return as-is (already valid)
return gtin_clean
elif length > 14:
if length > 14:
# Too long - truncate to EAN-13
logger.debug(f"GTIN too long ({length} digits), truncating: {gtin_clean}")
return gtin_clean[-13:]
elif 0 < length < 14:
if 0 < length < 14:
# Non-standard length - pad to EAN-13 (European standard)
# EAN-13 is the international standard used in Europe and most of the world
# UPC-A (12 digits) is primarily US/Canada
logger.debug(f"GTIN non-standard ({length} digits), padding to EAN-13: {gtin_clean}")
logger.debug(
f"GTIN non-standard ({length} digits), padding to EAN-13: {gtin_clean}"
)
return gtin_clean.zfill(13)
logger.warning(f"Invalid GTIN format: '{gtin_value}'")

View File

@@ -25,8 +25,6 @@ _ENCRYPTION_SALT = b"wizamart_encryption_salt_v1"
class EncryptionError(Exception):
"""Raised when encryption or decryption fails."""
pass
class EncryptionService:
"""

View File

@@ -14,6 +14,7 @@ Supported languages:
- de: German
- lb: Luxembourgish
"""
import json
import logging
from functools import lru_cache
@@ -97,7 +98,7 @@ def load_translations(language: str) -> dict:
return {}
try:
with open(locale_file, "r", encoding="utf-8") as f:
with open(locale_file, encoding="utf-8") as f:
return json.load(f)
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON in translation file {locale_file}: {e}")
@@ -139,7 +140,11 @@ def get_nested_value(data: dict, key_path: str, default: str = None) -> str:
else:
return default if default is not None else key_path
return value if isinstance(value, str) else (default if default is not None else key_path)
return (
value
if isinstance(value, str)
else (default if default is not None else key_path)
)
def translate(
@@ -180,7 +185,9 @@ def translate(
try:
text = text.format(**kwargs)
except KeyError as e:
logger.warning(f"Missing interpolation variable in translation '{key}': {e}")
logger.warning(
f"Missing interpolation variable in translation '{key}': {e}"
)
return text