1616_DEV_PREFIX = "[DEV] "
1717
1818
19- class _ArticleSeed (TypedDict ):
19+ class _ArticleSeed (TypedDict , total = False ):
2020 title : str
2121 description : str
2222 pokemon_name : str | None
@@ -25,8 +25,12 @@ class _ArticleSeed(TypedDict):
2525 condition : str
2626 purchase_price : Decimal
2727 sell_price : Decimal | None
28+ sold_price : Decimal | None
29+ sale_source : str | None
2830 is_sold : bool
2931 sold_at : dt .datetime | None
32+ published_on_vinted : bool
33+ published_on_ebay : bool
3034 images : list [str ]
3135
3236
@@ -38,6 +42,121 @@ def _load_dotenv() -> None:
3842 load_dotenv (_ROOT / ".env" )
3943
4044
45+ def _specs () -> list [tuple [str , str , str ]]:
46+ """(pokemon, set_code, card_number) — long list for pagination / dashboard tests."""
47+ return [
48+ ("Pikachu" , "SV8" , "025/162" ),
49+ ("Mewtwo" , "M1L" , "131/086" ),
50+ ("Herbizarre" , "M1L" , "065/063" ),
51+ ("Évoli" , "SV-P" , "040" ),
52+ ("Dracaufeu" , "SV3" , "125/198" ),
53+ ("Tortank" , "SV3" , "184/198" ),
54+ ("Florizarre" , "SV3" , "003/198" ),
55+ ("Lucario" , "SV4" , "078/182" ),
56+ ("Amphinobi" , "SV2" , "054/198" ),
57+ ("Garchomp" , "SV4" , "096/182" ),
58+ ("Tyranocif" , "SV4" , "066/182" ),
59+ ("Métalosse" , "SV4" , "112/182" ),
60+ ("Zacian" , "SSH" , "138/202" ),
61+ ("Zamazenta" , "SSH" , "139/202" ),
62+ ("Éthernatos" , "SV4" , "141/182" ),
63+ ("Palkia" , "SV5" , "204/191" ),
64+ ("Dialga" , "SV5" , "205/191" ),
65+ ("Rayquaza" , "SV6" , "029/167" ),
66+ ("Latias" , "SV6" , "073/167" ),
67+ ("Latios" , "SV6" , "074/167" ),
68+ ("Mew" , "SV4" , "053/182" ),
69+ ("Celebi" , "SV4" , "004/182" ),
70+ ("Ho-Oh" , "SV4" , "144/182" ),
71+ ("Lugia" , "SV4" , "145/182" ),
72+ ("Kyogre" , "SV5" , "032/191" ),
73+ ("Groudon" , "SV5" , "033/191" ),
74+ ("Giratina" , "SV5" , "130/191" ),
75+ ("Arceus" , "SV5" , "166/191" ),
76+ ("Darkrai" , "SV5" , "077/191" ),
77+ ("Genesect" , "SV5" , "181/191" ),
78+ ("Volcanion" , "SV6" , "136/167" ),
79+ ("Magearna" , "SV6" , "131/167" ),
80+ ("Marshadow" , "SV6" , "080/167" ),
81+ ("Zeraora" , "SV6" , "152/167" ),
82+ ("Évoli V" , "SWSH" , "065/203" ),
83+ ("Évoli VMAX" , "SWSH" , "066/203" ),
84+ ("Pikachu V" , "SWSH" , "043/172" ),
85+ ("Pikachu VMAX" , "SWSH" , "044/172" ),
86+ ("Raichu" , "SV1" , "026/198" ),
87+ ("Raichu Alola" , "SV1" , "027/198" ),
88+ ("Mimiqui" , "SV2" , "097/198" ),
89+ ("Boréas" , "SV3" , "144/198" ),
90+ ("Fulguris" , "SV3" , "145/198" ),
91+ ("Démétéros" , "SV3" , "146/198" ),
92+ ("Kyurem" , "SV3" , "047/198" ),
93+ ("Reshiram" , "SV3" , "048/198" ),
94+ ("Zekrom" , "SV3" , "049/198" ),
95+ ("Nymphali" , "SV8" , "075/162" ),
96+ ("Aquali" , "SV8" , "076/162" ),
97+ ("Pyroli" , "SV8" , "077/162" ),
98+ ("Voltali" , "SV8" , "078/162" ),
99+ ("Phyllali" , "SV8" , "079/162" ),
100+ ("Givrali" , "SV8" , "080/162" ),
101+ ("Noctali" , "SV8" , "081/162" ),
102+ ("Mentali" , "SV8" , "082/162" ),
103+ ("Dimoret" , "SV8" , "083/162" ),
104+ ("Nigosier" , "SV8" , "084/162" ),
105+ ("Couverdure" , "SV8" , "085/162" ),
106+ ("Motisma" , "SV8" , "086/162" ),
107+ ("Motisma" , "SV8" , "087/162" ),
108+ ("Motisma" , "SV8" , "088/162" ),
109+ ]
110+
111+
112+ def _build_samples (now : dt .datetime ) -> list [_ArticleSeed ]:
113+ specs = _specs ()
114+ out : list [_ArticleSeed ] = []
115+ for i , (pokemon , set_code , card_number ) in enumerate (specs ):
116+ n = i + 1
117+ purchase = Decimal ("2.00" ) + Decimal (i % 17 ) * Decimal ("1.25" )
118+ sell = (purchase * Decimal ("1.35" )).quantize (Decimal ("0.01" ))
119+ # Mix: many sold vs in-stock; varied marketplace flags
120+ sold = i % 5 != 0 and i % 7 != 1
121+ vinted_pub = not sold and (i % 3 == 0 or i % 11 == 2 )
122+ ebay_pub = not sold and (i % 4 == 1 ) and (i % 6 != 0 )
123+
124+ sold_at : dt .datetime | None = None
125+ sold_price : Decimal | None = None
126+ sale_src : str | None = None
127+ if sold :
128+ sold_at = now - dt .timedelta (days = (i % 90 ) + 1 )
129+ sale_src = "ebay" if i % 3 == 0 else "vinted"
130+ # Realized price sometimes slightly below listed price
131+ sold_price = (sell - Decimal ("0.50" )) if i % 5 == 0 else sell
132+
133+ seed_url = f"https://picsum.photos/seed/goupix-dev-{ n } /400/560"
134+ imgs : list [str ] = [] if i % 9 == 0 else [seed_url ]
135+ if i % 13 == 0 :
136+ imgs = [seed_url , f"https://picsum.photos/seed/goupix-dev-{ n } b/400/560" ]
137+
138+ row : _ArticleSeed = {
139+ "title" : f"{ _DEV_PREFIX } { pokemon } — { set_code } { card_number } " ,
140+ "description" : f"Seed dev #{ n } \n Série { set_code } \n État Near Mint" ,
141+ "pokemon_name" : pokemon ,
142+ "set_code" : set_code ,
143+ "card_number" : card_number ,
144+ "condition" : "Near Mint" ,
145+ "purchase_price" : purchase ,
146+ "sell_price" : None if sold and i % 8 == 3 else sell ,
147+ "sold_price" : sold_price ,
148+ "sale_source" : sale_src ,
149+ "is_sold" : sold ,
150+ "sold_at" : sold_at ,
151+ "published_on_vinted" : vinted_pub ,
152+ "published_on_ebay" : ebay_pub ,
153+ "images" : imgs ,
154+ }
155+ out .append (row )
156+
157+ return out
158+
159+
41160def main () -> None :
42161 _load_dotenv ()
43162
@@ -75,78 +194,26 @@ def main() -> None:
75194 print (f"Removed { removed } previous dev article(s)." )
76195
77196 now = dt .datetime .now (dt .UTC )
78- samples : list [_ArticleSeed ] = [
79- {
80- "title" : f"{ _DEV_PREFIX } Pikachu — Couronne Stellaire SV8 025/162 NM" ,
81- "description" : "Langue : Japonais\n Série : Couronne Stellaire\n État : Near Mint\n Carte de test seed." ,
82- "pokemon_name" : "Pikachu" ,
83- "set_code" : "SV8" ,
84- "card_number" : "025/162" ,
85- "condition" : "Near Mint" ,
86- "purchase_price" : Decimal ("3.50" ),
87- "sell_price" : None ,
88- "is_sold" : False ,
89- "sold_at" : None ,
90- "images" : ["https://picsum.photos/seed/goupix-pika/400/560" ],
91- },
92- {
93- "title" : f"{ _DEV_PREFIX } Mewtwo ex — M1L 131/086" ,
94- "description" : "Exemple listing FR / test données.\n Numéro : 131/086\n État : Near Mint" ,
95- "pokemon_name" : "Mewtwo" ,
96- "set_code" : "M1L" ,
97- "card_number" : "131/086" ,
98- "condition" : "Near Mint" ,
99- "purchase_price" : Decimal ("12.00" ),
100- "sell_price" : Decimal ("22.90" ),
101- "is_sold" : True ,
102- "sold_at" : now ,
103- "images" : [
104- "https://picsum.photos/seed/goupix-m2a/400/560" ,
105- "https://picsum.photos/seed/goupix-m2b/400/560" ,
106- ],
107- },
108- {
109- "title" : f"{ _DEV_PREFIX } Herbizarre AR — Mega Brave 065/063" ,
110- "description" : "Langue : Japonais\n Nom : Herbizarre / Ivysaur\n Numéro : 065/063 AR" ,
111- "pokemon_name" : "Herbizarre" ,
112- "set_code" : "M1L" ,
113- "card_number" : "065/063" ,
114- "condition" : "Near Mint" ,
115- "purchase_price" : Decimal ("8.25" ),
116- "sell_price" : None ,
117- "is_sold" : False ,
118- "sold_at" : None ,
119- "images" : [],
120- },
121- {
122- "title" : f"{ _DEV_PREFIX } Évoli promo — SV-P 040" ,
123- "description" : "Carte promo seed.\n État : Near Mint" ,
124- "pokemon_name" : "Évoli" ,
125- "set_code" : "SV-P" ,
126- "card_number" : "040" ,
127- "condition" : "Near Mint" ,
128- "purchase_price" : Decimal ("1.00" ),
129- "sell_price" : None ,
130- "is_sold" : False ,
131- "sold_at" : None ,
132- "images" : ["https://picsum.photos/seed/goupix-eevee/400/560" ],
133- },
134- ]
197+ samples = _build_samples (now )
135198
136199 for s in samples :
137- imgs = s [ "images" ]
200+ imgs = s . get ( "images" ) or [ ]
138201 article = Article (
139202 user_id = user .id ,
140203 title = s ["title" ],
141204 description = s ["description" ],
142- pokemon_name = s [ "pokemon_name" ] ,
143- set_code = s [ "set_code" ] ,
144- card_number = s [ "card_number" ] ,
145- condition = s [ "condition" ] ,
205+ pokemon_name = s . get ( "pokemon_name" ) ,
206+ set_code = s . get ( "set_code" ) ,
207+ card_number = s . get ( "card_number" ) ,
208+ condition = s . get ( "condition" ) or "Near Mint" ,
146209 purchase_price = s ["purchase_price" ],
147- sell_price = s ["sell_price" ],
148- is_sold = s ["is_sold" ],
149- sold_at = s ["sold_at" ],
210+ sell_price = s .get ("sell_price" ),
211+ sold_price = s .get ("sold_price" ),
212+ sale_source = s .get ("sale_source" ),
213+ is_sold = bool (s .get ("is_sold" )),
214+ sold_at = s .get ("sold_at" ),
215+ published_on_vinted = bool (s .get ("published_on_vinted" )),
216+ published_on_ebay = bool (s .get ("published_on_ebay" )),
150217 )
151218 db .add (article )
152219 db .flush ()
0 commit comments