- Add new database columns (coverImage, priceAmount, priceUnit, tags, status) to HotelGroup model - Create hotel_group_dict serializer to handle image/coverImage synchronization and tag formatting - Update site config endpoints to use the new serializer and filter published hotel groups - Add Alembic migration for the new database schema changes - Update test fixtures and API contract tests for the new fields - Revise public and admin API documentation to document the new hotel group fields and usage rules
98 lines
3.2 KiB
Python
98 lines
3.2 KiB
Python
from datetime import datetime
|
|
from sqlalchemy.inspection import inspect
|
|
|
|
|
|
def encode_value(value):
|
|
if isinstance(value, datetime):
|
|
return value.isoformat()
|
|
if isinstance(value, list):
|
|
return [encode_value(item) for item in value]
|
|
if isinstance(value, dict):
|
|
return {key: encode_value(item) for key, item in value.items()}
|
|
return value
|
|
|
|
|
|
def model_dict(instance, include: dict[str, object] | None = None) -> dict:
|
|
data = {column.key: encode_value(getattr(instance, column.key)) for column in inspect(instance).mapper.column_attrs}
|
|
if include:
|
|
for key, value in include.items():
|
|
data[key] = encode_value(value)
|
|
return data
|
|
|
|
|
|
def _sorted_aliases(destination) -> list:
|
|
return sorted(destination.aliases or [], key=lambda item: ((item.alias or "").lower(), item.id or ""))
|
|
|
|
|
|
def _sorted_images(product) -> list:
|
|
return sorted(product.images or [], key=lambda item: (item.sortOrder, item.id or "", item.url))
|
|
|
|
|
|
def _product_images(product) -> list[dict]:
|
|
return [model_dict(image) for image in _sorted_images(product)]
|
|
|
|
|
|
def _detail_sections(product) -> list:
|
|
return encode_value(product.detailSections or [])
|
|
|
|
|
|
def admin_product_dict(product) -> dict:
|
|
return model_dict(
|
|
product,
|
|
{
|
|
"tags": list(product.tags or []),
|
|
"detailSections": _detail_sections(product),
|
|
"destination": destination_dict(product.destination) if product.destination else None,
|
|
"images": _product_images(product),
|
|
},
|
|
)
|
|
|
|
|
|
def public_product_dict(product) -> dict:
|
|
return {
|
|
"id": product.id,
|
|
"sourceId": product.sourceId,
|
|
"title": product.title,
|
|
"subtitle": product.subtitle,
|
|
"destination": {"id": product.destination.id, "name": product.destination.name} if product.destination else None,
|
|
"priceAmount": product.priceAmount,
|
|
"priceUnit": product.priceUnit,
|
|
"tags": list(product.tags or []),
|
|
"coverImage": product.coverImage,
|
|
"summary": product.summary,
|
|
"images": _product_images(product),
|
|
"detailSections": _detail_sections(product),
|
|
"status": product.status,
|
|
}
|
|
|
|
|
|
def product_dict(product) -> dict:
|
|
return admin_product_dict(product)
|
|
|
|
|
|
def hotel_group_dict(group) -> dict:
|
|
data = model_dict(group)
|
|
if not data.get("coverImage"):
|
|
data["coverImage"] = data.get("image")
|
|
if not data.get("image"):
|
|
data["image"] = data.get("coverImage")
|
|
data["tags"] = list(group.tags or [])
|
|
return data
|
|
|
|
|
|
def destination_dict(destination, include_count: bool = False, product_count: int | None = None) -> dict:
|
|
data = model_dict(destination, {"aliases": [model_dict(alias) for alias in _sorted_aliases(destination)]})
|
|
if include_count:
|
|
data["_count"] = {"products": product_count if product_count is not None else len(destination.products or [])}
|
|
return data
|
|
|
|
|
|
def lead_dict(lead) -> dict:
|
|
return model_dict(
|
|
lead,
|
|
{
|
|
"sourceProduct": {"id": lead.sourceProduct.id, "title": lead.sourceProduct.title} if lead.sourceProduct else None,
|
|
"assignedUser": {"id": lead.assignedUser.id, "name": lead.assignedUser.name} if lead.assignedUser else None,
|
|
},
|
|
)
|