BuyingAgent/backend/app/normalization/manual.py
Lars b276758ee1
All checks were successful
Deploy Development / deploy (push) Successful in 1m19s
Test Suite / pytest-backend (push) Successful in 31s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-checks (push) Successful in 28s
Stelle den Frontend-MVP und die v0.3-API auf develop bereit, damit Dev API und PWA zusammen deployen.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-04 13:04:59 +02:00

62 lines
3.1 KiB
Python

from decimal import Decimal
from typing import Any
from app.domain.enums import NormalizationStatus, ValueType
from app.domain.models import AttributeDefinition, AttributeStatement
from app.errors import AppError
class ManualNormalizer:
version = "manual-v1"
def validate(self, definition: AttributeDefinition, statement: AttributeStatement) -> AttributeStatement:
if statement.normalization_status == NormalizationStatus.UNKNOWN:
if statement.normalized_value is not None:
raise AppError(
422,
"NORMALIZATION_INCONSISTENT",
"unknown statements must not have a normalized_value",
)
return statement
if statement.normalization_status != NormalizationStatus.NORMALIZED:
raise AppError(422, "NORMALIZATION_STATUS", "unsupported normalization_status")
if statement.normalized_value is None:
raise AppError(422, "NORMALIZATION_MISSING", "normalized_value is required")
_assert_type(definition, statement.normalized_value)
if definition.value_type == ValueType.NUMBER:
number = Decimal(str(statement.normalized_value))
if definition.key == "weight_kg" and number <= 0:
raise AppError(422, "VALUE_RANGE", "weight_kg must be > 0")
if definition.minimum is not None and number < definition.minimum:
raise AppError(422, "VALUE_RANGE", f"{definition.key} is below the minimum")
if definition.maximum is not None and number > definition.maximum:
raise AppError(422, "VALUE_RANGE", f"{definition.key} is above the maximum")
if definition.canonical_unit and statement.unit != definition.canonical_unit:
raise AppError(
422,
"UNIT_MISMATCH",
"unit must match the canonical unit",
{"expected": definition.canonical_unit, "actual": statement.unit},
)
if definition.canonical_unit is None and statement.unit not in {None, ""}:
raise AppError(422, "UNIT_MISMATCH", "this attribute has no canonical unit")
return statement
def _assert_type(definition: AttributeDefinition, value: Any) -> None:
if definition.value_type == ValueType.NUMBER:
if isinstance(value, bool) or not isinstance(value, (int, float, Decimal)):
raise AppError(422, "TYPE_MISMATCH", "normalized_value must be a number")
return
if definition.value_type == ValueType.STRING:
if not isinstance(value, str):
raise AppError(422, "TYPE_MISMATCH", "normalized_value must be a string")
return
if definition.value_type == ValueType.BOOLEAN:
if not isinstance(value, bool):
raise AppError(422, "TYPE_MISMATCH", "normalized_value must be a boolean")
return
if definition.value_type == ValueType.STRING_LIST:
if not isinstance(value, list) or any(not isinstance(item, str) for item in value):
raise AppError(422, "TYPE_MISMATCH", "normalized_value must be a list of strings")