11from datetime import datetime
22from decimal import Decimal
33from functools import reduce
4+ from typing import Any
45
56from pydantic import computed_field
67from sqlalchemy import Index , UniqueConstraint
8+ from sqlalchemy .dialects .postgresql import JSONB
79from sqlmodel import Field , Relationship , SQLModel
810
911from solesearch_api .models .base import TimestampedModel
@@ -28,9 +30,11 @@ class Sneaker(SneakerBase, TimestampedModel, table=True):
2830 Index ("ix_sneaker_sku_brand" , "sku" , "brand" ),
2931 )
3032 id : int | None = Field (default = None , primary_key = True )
33+ last_observed : datetime | None = None
3134 stockx_id : str | None = None
3235 stadium_goods_id : str | None = None
3336 source : Platform | None = None
37+ meta : dict = Field (sa_type = JSONB , default_factory = dict )
3438
3539 # Relationships
3640 links : list ["Link" ] = Relationship (back_populates = "sneaker" , cascade_delete = True )
@@ -39,6 +43,9 @@ class Sneaker(SneakerBase, TimestampedModel, table=True):
3943 back_populates = "sneaker" ,
4044 cascade_delete = True ,
4145 )
46+ nike_launches : list ["NikeLaunch" ] = Relationship (
47+ back_populates = "sneaker" , cascade_delete = True
48+ )
4249
4350 def get_links (self ) -> list [str ]:
4451 return [link .url for link in self .links ]
@@ -109,31 +116,38 @@ class PaginatedSneakersPublic(SQLModel):
109116
110117class SneakerSize (TimestampedModel , table = True ):
111118 __table_args__ = (
112- UniqueConstraint ("sneaker_id" , "value" ),
113- Index ("ix_sneaker_id_value" , "sneaker_id" , "value" ),
119+ UniqueConstraint ("sneaker_id" , "value" , "size_standard" ),
120+ Index (
121+ "ix_sneaker_id_value_size_standard" ,
122+ "sneaker_id" ,
123+ "value" ,
124+ "size_standard" ,
125+ ),
114126 )
115127 id : int | None = Field (default = None , primary_key = True )
116- value : int
128+ value : str
129+ size_standard : SizeStandard
130+ meta : dict = Field (sa_type = JSONB , default_factory = dict )
117131
118132 sneaker_id : int | None = Field (default = None , foreign_key = "sneaker.id" )
119133 sneaker : Sneaker | None = Relationship (back_populates = "sizes" )
120134
121135 prices : list ["Price" ] = Relationship (back_populates = "sneaker_size" )
122136
123- def get_standardized (self , size_standard : SizeStandard = SizeStandard .MENS_US ):
124- if size_standard == SizeStandard .MENS_US :
125- return self .value
126-
127137
128- class Price (TimestampedModel , table = True ):
138+ class Price (SQLModel , table = True ):
139+ # Ordered by last_observed timestamp
129140 __table_args__ = (
130- UniqueConstraint ("sneaker_size_id" , "platform" ),
131141 Index ("ix_sneaker_size_id_platform" , "sneaker_size_id" , "platform" ),
142+ Index ("ix_amount" , "amount" ),
143+ Index ("ix_last_observed" , "last_observed" ),
144+ Index ("ix_first_observed" , "first_observed" ),
132145 )
133146 id : int | None = Field (default = None , primary_key = True )
134147 platform : Platform | None = None
135148 amount : int # Monetary values stored as US cents
136- observed_at : datetime | None = None
149+ first_observed : datetime | None = None
150+ last_observed : datetime | None = None
137151
138152 sneaker_size_id : int | None = Field (default = None , foreign_key = "sneakersize.id" )
139153 sneaker_size : SneakerSize | None = Relationship (back_populates = "prices" )
@@ -142,10 +156,6 @@ class Price(TimestampedModel, table=True):
142156 def in_dollars (self ) -> Decimal :
143157 return Decimal (self .amount ) / 100
144158
145- @property
146- def for_size (self ) -> int :
147- return self .sneaker_size .value
148-
149159
150160class Link (TimestampedModel , table = True ):
151161 __table_args__ = (
@@ -173,3 +183,26 @@ class Image(TimestampedModel, table=True):
173183
174184 sneaker_id : int | None = Field (default = None , foreign_key = "sneaker.id" )
175185 sneaker : Sneaker | None = Relationship (back_populates = "images" )
186+
187+
188+ class NikeLaunch (SQLModel , table = True ):
189+ __table_args__ = (
190+ UniqueConstraint ("product_id" , "launch_id" ),
191+ Index ("ix_product_id_launch_id" , "product_id" , "launch_id" ),
192+ )
193+ id : int | None = Field (default = None , primary_key = True )
194+
195+ sneaker_id : int | None = Field (default = None , foreign_key = "sneaker.id" )
196+ sneaker : Sneaker | None = Relationship (back_populates = "nike_launches" )
197+
198+ launch_id : str
199+ product_id : str
200+ launch_method : str | None = None
201+ payment_method : str | None = None
202+ launch_link : str | None = None
203+ start_entry_date : datetime | None = None
204+ stop_entry_date : datetime | None = None
205+ stop_notify_date : datetime | None = None
206+ merch_product_status : str | None = None
207+ is_launch_product : bool | None = None
208+ last_observed : datetime | None = None
0 commit comments