From 7ccdc1fa8388a57e576d5182d443cabd7c101b6a Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 18 Feb 2026 20:58:11 +0000 Subject: [PATCH] Decouple market: use shared.models for all cross-app imports - Replace blog.models import with shared.models equivalent - Convert market/models/market.py and market_place.py to re-export stubs - Update shared + glue submodule pointers Co-Authored-By: Claude Opus 4.6 --- app.py | 2 +- glue | 2 +- models/market.py | 446 +---------------------------------------- models/market_place.py | 53 +---- shared | 2 +- 5 files changed, 10 insertions(+), 495 deletions(-) diff --git a/app.py b/app.py index 342e888..713f4ac 100644 --- a/app.py +++ b/app.py @@ -55,7 +55,7 @@ async def market_context() -> dict: def create_app() -> "Quart": from models.market_place import MarketPlace - from blog.models.ghost_content import Post + from shared.models.ghost_content import Post app = create_base_app("market", context_fn=market_context) diff --git a/glue b/glue index fc14d83..ebce44e 160000 --- a/glue +++ b/glue @@ -1 +1 @@ -Subproject commit fc14d8323adcb404894c8ab875431c9d571cfabb +Subproject commit ebce44e9d9ae0938647290a1e98687ae79fd71eb diff --git a/models/market.py b/models/market.py index 87a6b72..65511e1 100644 --- a/models/market.py +++ b/models/market.py @@ -1,441 +1,7 @@ -# at top of persist_snapshot.py: -from datetime import datetime -from typing import Optional, List -from sqlalchemy.orm import Mapped, mapped_column, relationship - -from typing import List, Optional - -from sqlalchemy import ( - String, Text, Integer, ForeignKey, DateTime, Boolean, Numeric, - UniqueConstraint, Index, func +from shared.models.market import ( # noqa: F401 + Product, ProductLike, ProductImage, ProductSection, + NavTop, NavSub, Listing, ListingItem, + LinkError, LinkExternal, SubcategoryRedirect, ProductLog, + ProductLabel, ProductSticker, ProductAttribute, ProductNutrition, ProductAllergen, + CartItem, ) - -from shared.db.base import Base # you already import Base in app.py - - - -class Product(Base): - __tablename__ = "products" - - id: Mapped[int] = mapped_column(primary_key=True) - slug: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False) - - title: Mapped[Optional[str]] = mapped_column(String(512)) - image: Mapped[Optional[str]] = mapped_column(Text) - - description_short: Mapped[Optional[str]] = mapped_column(Text) - description_html: Mapped[Optional[str]] = mapped_column(Text) - - suma_href: Mapped[Optional[str]] = mapped_column(Text) - brand: Mapped[Optional[str]] = mapped_column(String(255)) - - rrp: Mapped[Optional[float]] = mapped_column(Numeric(12, 2)) - rrp_currency: Mapped[Optional[str]] = mapped_column(String(16)) - rrp_raw: Mapped[Optional[str]] = mapped_column(String(128)) - - price_per_unit: Mapped[Optional[float]] = mapped_column(Numeric(12, 4)) - price_per_unit_currency: Mapped[Optional[str]] = mapped_column(String(16)) - price_per_unit_raw: Mapped[Optional[str]] = mapped_column(String(128)) - - special_price: Mapped[Optional[float]] = mapped_column(Numeric(12, 2)) - special_price_currency: Mapped[Optional[str]] = mapped_column(String(16)) - special_price_raw: Mapped[Optional[str]] = mapped_column(String(128)) - - regular_price: Mapped[Optional[float]] = mapped_column(Numeric(12, 2)) - regular_price_currency: Mapped[Optional[str]] = mapped_column(String(16)) - regular_price_raw: Mapped[Optional[str]] = mapped_column(String(128)) - - oe_list_price: Mapped[Optional[float]] = mapped_column(Numeric(12, 2)) - - case_size_count: Mapped[Optional[int]] = mapped_column(Integer) - case_size_item_qty: Mapped[Optional[float]] = mapped_column(Numeric(12, 3)) - case_size_item_unit: Mapped[Optional[str]] = mapped_column(String(32)) - case_size_raw: Mapped[Optional[str]] = mapped_column(String(128)) - - created_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), - nullable=False, - server_default=func.now(), - ) - updated_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), - nullable=False, - server_default=func.now(), - ) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - images: Mapped[List["ProductImage"]] = relationship( - back_populates="product", - cascade="all, delete-orphan", - passive_deletes=True, - ) - sections: Mapped[List["ProductSection"]] = relationship( - back_populates="product", - cascade="all, delete-orphan", - passive_deletes=True, - ) - labels: Mapped[List["ProductLabel"]] = relationship( - cascade="all, delete-orphan", - passive_deletes=True, - ) - stickers: Mapped[List["ProductSticker"]] = relationship( - cascade="all, delete-orphan", - passive_deletes=True, - ) - - ean: Mapped[Optional[str]] = mapped_column(String(64)) - sku: Mapped[Optional[str]] = mapped_column(String(128)) - unit_size: Mapped[Optional[str]] = mapped_column(String(128)) - pack_size: Mapped[Optional[str]] = mapped_column(String(128)) - - attributes = relationship( - "ProductAttribute", - back_populates="product", - lazy="selectin", - cascade="all, delete-orphan", - ) - nutrition = relationship( - "ProductNutrition", - back_populates="product", - lazy="selectin", - cascade="all, delete-orphan", - ) - allergens = relationship( - "ProductAllergen", - back_populates="product", - lazy="selectin", - cascade="all, delete-orphan", - ) - - likes = relationship( - "ProductLike", - back_populates="product", - cascade="all, delete-orphan", - ) - cart_items: Mapped[List["CartItem"]] = relationship( - "CartItem", - back_populates="product", - cascade="all, delete-orphan", - ) - - # NEW: all order items that reference this product - order_items: Mapped[List["OrderItem"]] = relationship( - "OrderItem", - back_populates="product", - cascade="all, delete-orphan", - ) - -from sqlalchemy import Column - -class ProductLike(Base): - __tablename__ = "product_likes" - - id = Column(Integer, primary_key=True, autoincrement=True) - user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) - product_slug: Mapped[str] = mapped_column(ForeignKey("products.slug", ondelete="CASCADE")) - - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - product: Mapped["Product"] = relationship("Product", back_populates="likes", foreign_keys=[product_slug]) - - user = relationship("User", back_populates="liked_products") # optional, if you want reverse access - - -class ProductImage(Base): - __tablename__ = "product_images" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False) - url: Mapped[str] = mapped_column(Text, nullable=False) - position: Mapped[int] = mapped_column(Integer, nullable=False, default=0) - kind: Mapped[str] = mapped_column(String(16), nullable=False, default="gallery") - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - product: Mapped["Product"] = relationship(back_populates="images") - - __table_args__ = ( - UniqueConstraint("product_id", "url", "kind", name="uq_product_images_product_url_kind"), - Index("ix_product_images_position", "position"), - ) - -class ProductSection(Base): - __tablename__ = "product_sections" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column( - ForeignKey("products.id", ondelete="CASCADE"), - index=True, - nullable=False, - ) - title: Mapped[str] = mapped_column(String(255), nullable=False) - html: Mapped[str] = mapped_column(Text, nullable=False) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - product: Mapped["Product"] = relationship(back_populates="sections") - __table_args__ = ( - UniqueConstraint("product_id", "title", name="uq_product_sections_product_title"), - ) -# --- Nav & listings --- - -class NavTop(Base): - __tablename__ = "nav_tops" - id: Mapped[int] = mapped_column(primary_key=True) - label: Mapped[str] = mapped_column(String(255), nullable=False) - slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True) - market_id: Mapped[Optional[int]] = mapped_column( - Integer, - ForeignKey("market_places.id", ondelete="SET NULL"), - nullable=True, - index=True, - ) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - listings: Mapped[List["Listing"]] = relationship(back_populates="top", cascade="all, delete-orphan") - market = relationship("MarketPlace", back_populates="nav_tops") - - __table_args__ = (UniqueConstraint("label", "slug", name="uq_nav_tops_label_slug"),) - -class NavSub(Base): - __tablename__ = "nav_subs" - id: Mapped[int] = mapped_column(primary_key=True) - top_id: Mapped[int] = mapped_column(ForeignKey("nav_tops.id", ondelete="CASCADE"), index=True, nullable=False) - label: Mapped[Optional[str]] = mapped_column(String(255)) - slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True) - href: Mapped[Optional[str]] = mapped_column(Text) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - listings: Mapped[List["Listing"]] = relationship(back_populates="sub", cascade="all, delete-orphan") - - __table_args__ = (UniqueConstraint("top_id", "slug", name="uq_nav_subs_top_slug"),) - -class Listing(Base): - __tablename__ = "listings" - - id: Mapped[int] = mapped_column(primary_key=True) - - # Old slug-based fields (optional: remove) - # top_slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True) - # sub_slug: Mapped[Optional[str]] = mapped_column(String(255), index=True) - - top_id: Mapped[int] = mapped_column(ForeignKey("nav_tops.id", ondelete="CASCADE"), index=True, nullable=False) - sub_id: Mapped[Optional[int]] = mapped_column(ForeignKey("nav_subs.id", ondelete="CASCADE"), index=True) - - total_pages: Mapped[Optional[int]] = mapped_column(Integer) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - - top: Mapped["NavTop"] = relationship(back_populates="listings") - sub: Mapped[Optional["NavSub"]] = relationship(back_populates="listings") - - __table_args__ = ( - UniqueConstraint("top_id", "sub_id", name="uq_listings_top_sub"), - ) - -class ListingItem(Base): - __tablename__ = "listing_items" - id: Mapped[int] = mapped_column(primary_key=True) - listing_id: Mapped[int] = mapped_column(ForeignKey("listings.id", ondelete="CASCADE"), index=True, nullable=False) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - slug: Mapped[str] = mapped_column(String(255), nullable=False, index=True) - __table_args__ = (UniqueConstraint("listing_id", "slug", name="uq_listing_items_listing_slug"),) - -# --- Reports / redirects / logs --- - -class LinkError(Base): - __tablename__ = "link_errors" - id: Mapped[int] = mapped_column(primary_key=True) - product_slug: Mapped[Optional[str]] = mapped_column(String(255), index=True) - href: Mapped[Optional[str]] = mapped_column(Text) - text: Mapped[Optional[str]] = mapped_column(Text) - top: Mapped[Optional[str]] = mapped_column(String(255)) - sub: Mapped[Optional[str]] = mapped_column(String(255)) - target_slug: Mapped[Optional[str]] = mapped_column(String(255)) - type: Mapped[Optional[str]] = mapped_column(String(255)) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - -class LinkExternal(Base): - __tablename__ = "link_externals" - id: Mapped[int] = mapped_column(primary_key=True) - product_slug: Mapped[Optional[str]] = mapped_column(String(255), index=True) - href: Mapped[Optional[str]] = mapped_column(Text) - text: Mapped[Optional[str]] = mapped_column(Text) - host: Mapped[Optional[str]] = mapped_column(String(255)) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - -class SubcategoryRedirect(Base): - __tablename__ = "subcategory_redirects" - id: Mapped[int] = mapped_column(primary_key=True) - old_path: Mapped[str] = mapped_column(String(512), nullable=False, index=True) - new_path: Mapped[str] = mapped_column(String(512), nullable=False) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - -class ProductLog(Base): - __tablename__ = "product_logs" - id: Mapped[int] = mapped_column(primary_key=True) - slug: Mapped[Optional[str]] = mapped_column(String(255), index=True) - href_tried: Mapped[Optional[str]] = mapped_column(Text) - ok: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false") - error_type: Mapped[Optional[str]] = mapped_column(String(255)) - error_message: Mapped[Optional[str]] = mapped_column(Text) - http_status: Mapped[Optional[int]] = mapped_column(Integer) - final_url: Mapped[Optional[str]] = mapped_column(Text) - transport_error: Mapped[Optional[bool]] = mapped_column(Boolean) - title: Mapped[Optional[str]] = mapped_column(String(512)) - has_description_html: Mapped[Optional[bool]] = mapped_column(Boolean) - has_description_short: Mapped[Optional[bool]] = mapped_column(Boolean) - sections_count: Mapped[Optional[int]] = mapped_column(Integer) - images_count: Mapped[Optional[int]] = mapped_column(Integer) - embedded_images_count: Mapped[Optional[int]] = mapped_column(Integer) - all_images_count: Mapped[Optional[int]] = mapped_column(Integer) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - - - -# ...existing models... - -class ProductLabel(Base): - __tablename__ = "product_labels" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False) - name: Mapped[str] = mapped_column(String(255), nullable=False) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - product: Mapped["Product"] = relationship(back_populates="labels") - - __table_args__ = (UniqueConstraint("product_id", "name", name="uq_product_labels_product_name"),) - -class ProductSticker(Base): - __tablename__ = "product_stickers" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False) - name: Mapped[str] = mapped_column(String(255), nullable=False) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - product: Mapped["Product"] = relationship(back_populates="stickers") - - __table_args__ = (UniqueConstraint("product_id", "name", name="uq_product_stickers_product_name"),) - -class ProductAttribute(Base): - __tablename__ = "product_attributes" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False) - key: Mapped[str] = mapped_column(String(255), nullable=False) - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - - value: Mapped[Optional[str]] = mapped_column(Text) - product = relationship("Product", back_populates="attributes") - __table_args__ = (UniqueConstraint("product_id", "key", name="uq_product_attributes_product_key"),) - -class ProductNutrition(Base): - __tablename__ = "product_nutrition" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False) - key: Mapped[str] = mapped_column(String(255), nullable=False) - value: Mapped[Optional[str]] = mapped_column(String(255)) - unit: Mapped[Optional[str]] = mapped_column(String(64)) - product = relationship("Product", back_populates="nutrition") - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - __table_args__ = (UniqueConstraint("product_id", "key", name="uq_product_nutrition_product_key"),) - -class ProductAllergen(Base): - __tablename__ = "product_allergens" - id: Mapped[int] = mapped_column(primary_key=True) - product_id: Mapped[int] = mapped_column(ForeignKey("products.id", ondelete="CASCADE"), index=True, nullable=False) - name: Mapped[str] = mapped_column(String(255), nullable=False) - contains: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false") - created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) - deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) - product: Mapped["Product"] = relationship(back_populates="allergens") - __table_args__ = (UniqueConstraint("product_id", "name", name="uq_product_allergens_product_name"),) - - -class CartItem(Base): - __tablename__ = "cart_items" - - id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) - - # Either a logged-in user OR an anonymous session - user_id: Mapped[int | None] = mapped_column( - ForeignKey("users.id", ondelete="CASCADE"), - nullable=True, - ) - session_id: Mapped[str | None] = mapped_column( - String(128), - nullable=True, - ) - - # IMPORTANT: link to product *id*, not slug - product_id: Mapped[int] = mapped_column( - ForeignKey("products.id", ondelete="CASCADE"), - nullable=False, - ) - - quantity: Mapped[int] = mapped_column( - Integer, - nullable=False, - default=1, - server_default="1", - ) - - created_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), - nullable=False, - server_default=func.now(), - ) - updated_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), - nullable=False, - server_default=func.now(), - ) - market_place_id: Mapped[int | None] = mapped_column( - ForeignKey("market_places.id", ondelete="SET NULL"), - nullable=True, - index=True, - ) - - deleted_at: Mapped[datetime | None] = mapped_column( - DateTime(timezone=True), - nullable=True, - ) - - # Relationships - - market_place: Mapped["MarketPlace | None"] = relationship( - "MarketPlace", - foreign_keys=[market_place_id], - ) - product: Mapped["Product"] = relationship( - "Product", - back_populates="cart_items", - ) - user: Mapped["User | None"] = relationship("User", back_populates="cart_items") - - __table_args__ = ( - Index("ix_cart_items_user_product", "user_id", "product_id"), - Index("ix_cart_items_session_product", "session_id", "product_id"), - ) diff --git a/models/market_place.py b/models/market_place.py index 8792e36..ca65447 100644 --- a/models/market_place.py +++ b/models/market_place.py @@ -1,52 +1 @@ -from __future__ import annotations - -from datetime import datetime, timezone -from typing import Optional, List - -from sqlalchemy import ( - Integer, String, Text, DateTime, ForeignKey, Index, func, text, -) -from sqlalchemy.orm import Mapped, mapped_column, relationship - -from shared.db.base import Base - - -def utcnow() -> datetime: - return datetime.now(timezone.utc) - - -class MarketPlace(Base): - __tablename__ = "market_places" - - id: Mapped[int] = mapped_column(Integer, primary_key=True) - container_type: Mapped[str] = mapped_column( - String(32), nullable=False, server_default=text("'page'"), - ) - container_id: Mapped[int] = mapped_column(Integer, nullable=False) - name: Mapped[str] = mapped_column(String(255), nullable=False) - slug: Mapped[str] = mapped_column(String(255), nullable=False) - description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) - - created_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), nullable=False, server_default=func.now(), - ) - updated_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), nullable=False, server_default=func.now(), - ) - deleted_at: Mapped[Optional[datetime]] = mapped_column( - DateTime(timezone=True), nullable=True, - ) - - nav_tops: Mapped[List["NavTop"]] = relationship( - "NavTop", back_populates="market", - ) - - __table_args__ = ( - Index("ix_market_places_container", "container_type", "container_id"), - Index( - "ux_market_places_slug_active", - func.lower(slug), - unique=True, - postgresql_where=text("deleted_at IS NULL"), - ), - ) +from shared.models.market_place import MarketPlace # noqa: F401 diff --git a/shared b/shared index da10fc4..0c0f3c8 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit da10fc4cf91d355342decb2c47e4d4e279e981a1 +Subproject commit 0c0f3c84167ff61ad469e1f5ad93de7841c59e76