from datetime import datetime from sqlalchemy.inspection import inspect from .media_urls import resolve_media_url 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 media_model_dict(instance) -> dict: return resolve_media_fields(model_dict(instance)) def resolve_media_fields(data: dict) -> dict: result = dict(data) for field in ("image", "avatar", "qrImage", "url"): if field in result: result[field] = resolve_media_url(result[field]) for field in ("gallery", "images"): if field in result and isinstance(result[field], list): result[field] = [resolve_media_url(value) for value in result[field]] return result def lead_dict(lead) -> dict: return model_dict( lead, {"assignedUser": {"id": lead.assignedUser.id, "name": lead.assignedUser.name} if lead.assignedUser else None}, ) def public_wanfa_route_dict(route) -> dict: return { "id": route.id, "title": route.title, "subtitle": route.subtitle, "image": resolve_media_url(route.image), "routeCount": route.routeCount, "demandKeyword": route.demandKeyword, } def public_wanfa_category_dict(category) -> dict: return { "id": category.id, "label": category.label, "routes": [public_wanfa_route_dict(route) for route in category.routes], } def detail_record_dict(detail) -> dict: return media_model_dict(detail) def public_detail_dict(detail, concierge_advisor=None) -> dict: return { "key": detail.key, "eyebrow": detail.eyebrow, "duration": detail.duration, "title": detail.title, "subtitle": detail.subtitle, "intro": detail.intro, "highlights": detail.highlights or [], "included": detail.included or [], "excluded": detail.excluded or [], "notes": detail.notes or [], "gallery": [resolve_media_url(image) for image in (detail.gallery or [])], "priceStartingValue": detail.priceStartingValue, "priceUnit": detail.priceUnit, "pricePeopleRange": detail.pricePeopleRange, "priceDescription": detail.priceDescription, "conciergeAdvisor": public_concierge_advisor_dict(concierge_advisor) if concierge_advisor else None, } def public_home_wanfa_recommendation_dict(item) -> dict: return { "id": item.id, "categoryId": item.category.id, "label": item.category.label, "routes": [public_wanfa_route_dict(route) for route in item.category.routes], } def concierge_advisor_dict(advisor) -> dict: return media_model_dict(advisor) def public_concierge_advisor_dict(advisor) -> dict: return { "avatar": resolve_media_url(advisor.avatar), "name": advisor.name, "role": advisor.role, "details": advisor.details or [], "qrImage": resolve_media_url(advisor.qrImage), } def public_home_experience_dict(item) -> dict: return { "id": item.id, "badge": item.badge, "category": item.category, "title": item.title, "englishTitle": item.englishTitle, "image": resolve_media_url(item.image), "demandKeyword": item.demandKeyword, } def public_home_team_building_dict(item) -> dict: return { "id": item.id, "tag": item.tag, "title": item.title, "description": item.description, "image": resolve_media_url(item.image), "demandKeyword": item.demandKeyword, } def public_home_team_building_detail_dict(item) -> dict: paragraphs = [ paragraph.strip() for paragraph in (getattr(item, "detailParagraphs", None) or []) if isinstance(paragraph, str) and paragraph.strip() ] if not paragraphs: paragraphs = [item.description] return { **public_home_team_building_dict(item), "detailSubtitle": (getattr(item, "detailSubtitle", None) or item.description).strip(), "detailParagraphs": paragraphs, } def public_home_wild_archive_dict(item) -> dict: return { "id": item.id, "title": item.title, "image": resolve_media_url(item.image), "demandKeyword": item.demandKeyword, "photoCount": len(item.images or [item.image]), } def public_home_wild_archive_detail_dict(item) -> dict: images = [image for image in (item.images or []) if isinstance(image, str) and image.strip()] if not images: images = [item.image] return { **public_home_wild_archive_dict(item), "images": [resolve_media_url(image) for image in images], }