Theflame47 commited on
Commit
ad8645f
·
verified ·
1 Parent(s): a5c603d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -216
app.py CHANGED
@@ -1,8 +1,7 @@
1
  import os
2
  import time
3
  import json
4
- import tempfile
5
- from typing import Dict, Any, List, Generator, Tuple, Optional
6
 
7
  import requests
8
  import gradio as gr
@@ -42,241 +41,122 @@ def _log(logs: List[str], msg: str):
42
  logs.append(f"[{_now()}] {msg}")
43
 
44
 
45
- def _list_shops(logs: List[str]) -> List[Dict[str, Any]]:
46
- _log(logs, "Fetching shops: GET /v1/shops.json")
47
- shops = _req("GET", "/v1/shops.json")
48
- if not isinstance(shops, list):
49
- raise RuntimeError("Unexpected shops response")
50
- _log(logs, f"Found {len(shops)} shops")
51
- return shops
52
-
53
-
54
- def _paginate_blueprints(logs: List[str], per_page: int = 100) -> List[Dict[str, Any]]:
55
- out: List[Dict[str, Any]] = []
56
- page = 1
57
- while True:
58
- _log(logs, f"Listing blueprints page={page} per_page={per_page}")
59
- data = _req("GET", f"/v1/catalog/blueprints.json?page={page}&per_page={per_page}")
60
- if not isinstance(data, list) or not data:
61
- break
62
- out.extend(data)
63
- page += 1
64
- if page > 9999:
65
- break
66
- _log(logs, f"Total blueprints fetched: {len(out)}")
67
- return out
68
-
69
-
70
- def _extract_variant_snapshot(variants: List[Dict[str, Any]]) -> Tuple[List[Dict[str, Any]], float]:
71
- snapshot = []
72
- all_cents: List[int] = []
73
 
74
- for v in variants:
75
- cents_raw = v.get("price")
76
- cents = None
77
- try:
78
- if isinstance(cents_raw, (int, float)):
79
- cents = int(cents_raw)
80
- elif isinstance(cents_raw, str) and cents_raw.isdigit():
81
- cents = int(cents_raw)
82
- except Exception:
83
- cents = None
84
-
85
- if cents is not None:
86
- all_cents.append(cents)
87
-
88
- opts = v.get("options") or {}
89
- snapshot.append(
90
- {
91
- "id": v.get("id"),
92
- "sku": v.get("sku"),
93
- "size": opts.get("size"),
94
- "color": opts.get("color"),
95
- "priceCents": cents,
96
- "price": round(cents / 100, 2) if cents is not None else None,
97
- "placeholders": v.get("placeholders") or [],
98
- }
99
- )
100
 
101
- min_price = round(min(all_cents) / 100, 2) if all_cents else DEFAULT_BASE_PRICE
102
- return snapshot, min_price
 
 
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- def run_phase_a(
106
- selected_shop_id: Optional[str],
107
- max_templates: int,
108
- per_page: int,
109
- ) -> Generator[Tuple[str, str, Optional[str]], None, None]:
110
- logs: List[str] = []
111
- results: List[Dict[str, Any]] = []
112
- download_path: Optional[str] = None
113
 
114
- def flush():
115
- return "\n".join(logs), json.dumps(results[:3], indent=2), download_path
116
 
117
- try:
118
- _log(logs, "START")
119
- yield flush()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- shops = _list_shops(logs)
122
- if not selected_shop_id:
123
- raise RuntimeError("Pick a shop (Atheria) first, then Run.")
124
- shop_id = str(selected_shop_id)
125
 
126
- hit = None
127
- for s in shops:
128
- if str(s.get("id")) == shop_id:
129
- hit = s
130
- break
131
 
132
- if hit:
133
- _log(logs, f"Selected shop: title={hit.get('title') or 'untitled'} id={shop_id} channel={hit.get('sales_channel') or 'unknown'}")
134
- else:
135
- _log(logs, f"Selected shop_id={shop_id} (not found in list_shops response?)")
136
 
137
- yield flush()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- max_templates = int(max_templates or 0)
140
- per_page = int(per_page or 100)
141
 
142
- blueprints = _paginate_blueprints(logs, per_page=per_page)
 
143
  yield flush()
144
 
145
- pairs_scanned = 0
146
-
147
- for bp in blueprints:
148
- bp_id = bp.get("id")
149
- if bp_id is None:
150
- continue
151
- bp_id_s = str(bp_id)
152
-
153
- _log(logs, f"Blueprint {bp_id_s}: fetching providers")
154
- providers = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}/print_providers.json")
155
- if not isinstance(providers, list) or not providers:
156
- continue
157
-
158
- for p in providers:
159
- p_id = p.get("id")
160
- if p_id is None:
161
- continue
162
- p_id_s = str(p_id)
163
-
164
- pairs_scanned += 1
165
- _log(logs, f"Blueprint {bp_id_s} / Provider {p_id_s}: fetching variants")
166
-
167
- vr = _req(
168
- "GET",
169
- f"/v1/catalog/blueprints/{bp_id_s}/print_providers/{p_id_s}/variants.json?show-out-of-stock=1",
170
- )
171
- variants = vr.get("variants") if isinstance(vr, dict) else None
172
- if not isinstance(variants, list) or not variants:
173
- continue
174
-
175
- _log(logs, f"FOUND {len(variants)} variants (bp={bp_id_s}, provider={p_id_s})")
176
-
177
- blueprint_details = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}.json")
178
- provider_details = _req("GET", f"/v1/catalog/print_providers/{p_id_s}.json")
179
- shipping_info = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}/print_providers/{p_id_s}/shipping.json")
180
-
181
- variant_snapshot, min_price = _extract_variant_snapshot(variants)
182
-
183
- _log(logs, "VARIANT_SNAPSHOT " + json.dumps({"count": len(variant_snapshot), "sample": variant_snapshot[:20]}))
184
-
185
- provider_loc = provider_details.get("location") or {}
186
- provider_name = provider_details.get("title") or provider_details.get("name") or "empty"
187
- loc_str = ", ".join([x for x in [provider_loc.get("city"), provider_loc.get("country")] if x]) or "empty"
188
- base_desc = blueprint_details.get("description") or bp.get("description") or "empty"
189
- description = f"{base_desc}\n\nFulfilled by {provider_name} — {loc_str}"
190
-
191
- colors = sorted({v.get("color") for v in variant_snapshot if v.get("color")})
192
- sizes = sorted({v.get("size") for v in variant_snapshot if v.get("size")})
193
-
194
- product_template = {
195
- "shop_id": int(shop_id),
196
- "blueprint_id": int(bp_id_s),
197
- "print_provider_id": int(p_id_s),
198
- "name": blueprint_details.get("title") or bp.get("title") or "empty",
199
- "description": description,
200
- "tags": ["Printify", f"Provider: {provider_name}"],
201
- "options": {"Color": colors, "Size": sizes},
202
- "variants_count": len(variant_snapshot),
203
- "price_min": min_price,
204
- "variants": variant_snapshot,
205
- "shipping": shipping_info,
206
- "raw": {
207
- "blueprint": bp,
208
- "provider": p,
209
- "blueprintDetails": blueprint_details,
210
- "providerDetails": provider_details,
211
- },
212
- }
213
 
214
- results.append(product_template)
215
- yield flush()
216
-
217
- if max_templates and len(results) >= max_templates:
218
- _log(logs, f"Reached max_templates={max_templates}. Stopping.")
219
- break
220
-
221
- if max_templates and len(results) >= max_templates:
222
- break
223
-
224
- _log(logs, f"DONE templates={len(results)} pairs_scanned={pairs_scanned}")
225
-
226
- fd, path = tempfile.mkstemp(prefix="phase_a_", suffix=".json")
227
- os.close(fd)
228
- with open(path, "w", encoding="utf-8") as f:
229
- json.dump(
230
- {
231
- "shop_id": int(shop_id),
232
- "generated_at": time.strftime("%Y-%m-%d %H:%M:%S"),
233
- "templates": results,
234
- },
235
- f,
236
- indent=2,
237
- )
238
- download_path = path
239
  yield flush()
240
 
241
  except Exception as e:
242
  _log(logs, f"ERROR: {e}")
243
- yield "\n".join(logs), "[]", None
244
-
245
-
246
- def fetch_shops_for_dropdown() -> Tuple[str, dict]:
247
- logs: List[str] = []
248
- shops = _list_shops(logs)
249
- choices = []
250
- for s in shops:
251
- sid = s.get("id")
252
- title = s.get("title") or "untitled"
253
- chan = s.get("sales_channel") or "unknown"
254
- choices.append((f"{title} ({chan}) — {sid}", str(sid)))
255
- return "\n".join(logs), gr.update(choices=choices, value=None)
256
-
257
-
258
- with gr.Blocks(title="Printify Phase A Probe") as demo:
259
- gr.Markdown("Phase A: fetch shop ID (pick Atheria) + enumerate blueprint/provider templates (variants + placeholders) + download JSON.")
260
-
261
- shops_log = gr.Textbox(label="Shop Logs", lines=6)
262
- shop_dropdown = gr.Dropdown(label="Select Shop (pick Atheria)", choices=[], value=None)
263
- fetch_shops_btn = gr.Button("Fetch Shops")
264
-
265
- max_templates = gr.Number(label="Max templates (0 = no limit)", value=25, precision=0)
266
- per_page = gr.Number(label="Blueprints per page", value=100, precision=0)
267
 
268
- run_btn = gr.Button("Run Phase A")
269
 
270
- logs = gr.Textbox(label="Logs", lines=16)
271
- preview = gr.Textbox(label="Preview (first 3 templates)", lines=16)
272
- download = gr.File(label="Download Full Phase A JSON")
273
 
274
- fetch_shops_btn.click(fetch_shops_for_dropdown, inputs=[], outputs=[shops_log, shop_dropdown])
 
 
 
275
 
276
- run_btn.click(
277
- run_phase_a,
278
- inputs=[shop_dropdown, max_templates, per_page],
279
- outputs=[logs, preview, download],
280
- )
281
 
282
- demo.queue().launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
  import time
3
  import json
4
+ from typing import Dict, Any, List, Generator, Tuple
 
5
 
6
  import requests
7
  import gradio as gr
 
41
  logs.append(f"[{_now()}] {msg}")
42
 
43
 
44
+ def _find_first_valid_pair(logs: List[str]) -> Dict[str, Any]:
45
+ _log(logs, "Listing blueprints")
46
+ blueprints = _req("GET", "/v1/catalog/blueprints.json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ for bp in blueprints:
49
+ bp_id = str(bp["id"])
50
+ _log(logs, f"Blueprint {bp_id}: fetching providers")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ providers = _req("GET", f"/v1/catalog/blueprints/{bp_id}/print_providers.json")
53
+ for p in providers:
54
+ p_id = str(p["id"])
55
+ _log(logs, f"Blueprint {bp_id} / Provider {p_id}: fetching variants")
56
 
57
+ vr = _req(
58
+ "GET",
59
+ f"/v1/catalog/blueprints/{bp_id}/print_providers/{p_id}/variants.json?show-out-of-stock=1",
60
+ )
61
+ variants = vr.get("variants")
62
+ if isinstance(variants, list) and variants:
63
+ _log(logs, f"FOUND {len(variants)} variants")
64
+
65
+ return {
66
+ "blueprint": bp,
67
+ "provider": p,
68
+ "variants": variants,
69
+ "blueprintDetails": _req("GET", f"/v1/catalog/blueprints/{bp_id}.json"),
70
+ "providerDetails": _req("GET", f"/v1/catalog/print_providers/{p_id}.json"),
71
+ "shippingInfo": _req(
72
+ "GET",
73
+ f"/v1/catalog/blueprints/{bp_id}/print_providers/{p_id}/shipping.json",
74
+ ),
75
+ }
76
 
77
+ raise RuntimeError("No blueprint/provider pair with variants found.")
 
 
 
 
 
 
 
78
 
 
 
79
 
80
+ def _build_product(blob: Dict[str, Any], currency: str, logs: List[str]) -> Dict[str, Any]:
81
+ variants = blob["variants"]
82
+
83
+ snapshot = []
84
+ for v in variants:
85
+ cents = v.get("price")
86
+ cents = int(cents) if isinstance(cents, (int, str)) and str(cents).isdigit() else None
87
+ snapshot.append({
88
+ "id": v.get("id"),
89
+ "sku": v.get("sku"),
90
+ "size": (v.get("options") or {}).get("size"),
91
+ "color": (v.get("options") or {}).get("color"),
92
+ "priceCents": cents,
93
+ "price": round(cents / 100, 2) if cents is not None else None,
94
+ })
95
+
96
+ _log(logs, f"VARIANT_SNAPSHOT sample={json.dumps(snapshot[:20])}")
97
+
98
+ all_cents = [v["priceCents"] for v in snapshot if v["priceCents"] is not None]
99
+ min_price = round(min(all_cents) / 100, 2) if all_cents else DEFAULT_BASE_PRICE
100
 
101
+ colors = sorted({v["color"] for v in snapshot if v["color"]})
102
+ sizes = sorted({v["size"] for v in snapshot if v["size"]})
 
 
103
 
104
+ provider = blob["providerDetails"]
105
+ loc = provider.get("location") or {}
106
+ loc_str = ", ".join(x for x in [loc.get("city"), loc.get("country")] if x) or "empty"
 
 
107
 
108
+ description = (
109
+ (blob["blueprintDetails"].get("description") or "empty")
110
+ + f"\n\nFulfilled by {provider.get('title') or provider.get('name')} — {loc_str}"
111
+ )
112
 
113
+ return {
114
+ "name": blob["blueprintDetails"].get("title"),
115
+ "currency": currency,
116
+ "price": min_price,
117
+ "tags": ["Printify", f"Provider: {provider.get('title') or provider.get('name')}"],
118
+ "options": {
119
+ "Color": colors,
120
+ "Size": sizes,
121
+ },
122
+ "description": description,
123
+ "variants": snapshot,
124
+ "raw": blob,
125
+ }
126
+
127
+
128
+ def run(currency: str) -> Generator[Tuple[str, str], None, None]:
129
+ logs: List[str] = []
130
+ result: Dict[str, Any] = {}
131
 
132
+ def flush():
133
+ return "\n".join(logs), json.dumps(result, indent=2)
134
 
135
+ try:
136
+ _log(logs, "START")
137
  yield flush()
138
 
139
+ blob = _find_first_valid_pair(logs)
140
+ yield flush()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
+ result = _build_product(blob, currency or "USD", logs)
143
+ _log(logs, "DONE")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  yield flush()
145
 
146
  except Exception as e:
147
  _log(logs, f"ERROR: {e}")
148
+ result = {"error": str(e)}
149
+ yield flush()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
 
151
 
152
+ with gr.Blocks(title="Printify Catalog Probe") as demo:
153
+ gr.Markdown("Extract and normalize ONE Printify blueprint/provider into a structured JSON object.")
 
154
 
155
+ currency = gr.Textbox(label="Currency", value="USD")
156
+ btn = gr.Button("Run")
157
+ logs = gr.Textbox(label="Logs", lines=18)
158
+ out = gr.Textbox(label="Output JSON", lines=18)
159
 
160
+ btn.click(run, inputs=[currency], outputs=[logs, out])
 
 
 
 
161
 
162
+ demo.queue(concurrency_count=1).launch()