Theflame47 commited on
Commit
d20f3e3
·
verified ·
1 Parent(s): 096a4ec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -74
app.py CHANGED
@@ -2,26 +2,28 @@ import os
2
  import time
3
  import json
4
  import tempfile
5
- from typing import Dict, Any, List, Tuple, Generator, Optional
6
 
7
  import requests
8
  import gradio as gr
9
 
10
  PRINTIFY_BASE = "https://api.printify.com"
 
11
 
12
 
13
  def _now() -> str:
14
  return time.strftime("%H:%M:%S")
15
 
16
 
 
 
 
 
17
  def _auth_headers() -> Dict[str, str]:
18
  token = os.environ.get("PRINTIFY_API_TOKEN")
19
  if not token:
20
  raise RuntimeError("Missing PRINTIFY_API_TOKEN (HF Space Secret).")
21
- return {
22
- "Authorization": f"Bearer {token}",
23
- "User-Agent": "PrintifyPhaseAProbe/1.0",
24
- }
25
 
26
 
27
  def _req(method: str, path: str) -> Any:
@@ -40,18 +42,13 @@ def _log(logs: List[str], msg: str):
40
  logs.append(f"[{_now()}] {msg}")
41
 
42
 
43
- def list_shops() -> Tuple[str, List[Tuple[str, str]]]:
44
- logs: List[str] = []
45
  _log(logs, "Fetching shops: GET /v1/shops.json")
46
  shops = _req("GET", "/v1/shops.json")
47
- choices: List[Tuple[str, str]] = []
48
- for s in shops or []:
49
- sid = str(s.get("id"))
50
- title = s.get("title") or "untitled"
51
- chan = s.get("sales_channel") or "unknown"
52
- choices.append((f"{title} ({chan}) — {sid}", sid))
53
- _log(logs, f"Found {len(choices)} shops")
54
- return "\n".join(logs), choices
55
 
56
 
57
  def _paginate_blueprints(logs: List[str], per_page: int = 100) -> List[Dict[str, Any]]:
@@ -70,9 +67,44 @@ def _paginate_blueprints(logs: List[str], per_page: int = 100) -> List[Dict[str,
70
  return out
71
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  def run_phase_a(
74
- shop_id: str,
75
- max_pairs: int,
76
  per_page: int,
77
  ) -> Generator[Tuple[str, str, Optional[str]], None, None]:
78
  logs: List[str] = []
@@ -83,81 +115,108 @@ def run_phase_a(
83
  return "\n".join(logs), json.dumps(results[:3], indent=2), download_path
84
 
85
  try:
86
- if not shop_id:
87
- raise RuntimeError("Pick a shop first (Atheria).")
 
 
 
 
 
 
 
 
88
 
89
- _log(logs, f"START Phase A (shop_id={shop_id})")
 
 
 
 
 
90
  yield flush()
91
 
 
 
 
92
  blueprints = _paginate_blueprints(logs, per_page=per_page)
93
  yield flush()
94
 
95
  pairs_scanned = 0
96
 
97
  for bp in blueprints:
98
- bp_id = str(bp.get("id"))
99
- if not bp_id:
100
  continue
 
101
 
102
- providers = _req("GET", f"/v1/catalog/blueprints/{bp_id}/print_providers.json")
 
103
  if not isinstance(providers, list) or not providers:
104
  continue
105
 
106
  for p in providers:
107
- p_id = str(p.get("id"))
108
- if not p_id:
109
  continue
 
110
 
111
  pairs_scanned += 1
112
- _log(logs, f"Pair {pairs_scanned}: blueprint={bp_id} provider={p_id}")
113
 
114
- vr = _req("GET", f"/v1/catalog/blueprints/{bp_id}/print_providers/{p_id}/variants.json")
115
  variants = vr.get("variants") if isinstance(vr, dict) else None
116
  if not isinstance(variants, list) or not variants:
117
  continue
118
 
119
- _log(logs, f"FOUND variants={len(variants)} for blueprint={bp_id} provider={p_id}")
120
-
121
- blueprint_details = _req("GET", f"/v1/catalog/blueprints/{bp_id}.json")
122
- provider_details = _req("GET", f"/v1/catalog/print_providers/{p_id}.json")
123
- shipping_info = _req("GET", f"/v1/catalog/blueprints/{bp_id}/print_providers/{p_id}/shipping.json")
124
-
125
- variant_snapshot = []
126
- for v in variants:
127
- opts = (v.get("options") or {})
128
- variant_snapshot.append({
129
- "id": v.get("id"),
130
- "title": v.get("title"),
131
- "options": {
132
- "color": opts.get("color"),
133
- "size": opts.get("size"),
134
- },
135
- "placeholders": v.get("placeholders") or [],
136
- })
137
-
138
- results.append({
139
- "blueprint_id": int(bp_id),
140
- "print_provider_id": int(p_id),
141
- "blueprint_title": blueprint_details.get("title") or bp.get("title"),
142
- "brand": blueprint_details.get("brand") or bp.get("brand"),
143
- "model": blueprint_details.get("model") or bp.get("model"),
144
- "provider_title": provider_details.get("title") or p.get("title"),
145
- "provider_location": provider_details.get("location") or {},
146
- "variants_count": len(variants),
 
147
  "variants": variant_snapshot,
148
  "shipping": shipping_info,
149
- })
150
-
 
 
 
 
 
 
 
151
  yield flush()
152
 
153
- if max_pairs and len(results) >= max_pairs:
154
- _log(logs, f"Reached max_pairs={max_pairs}. Stopping.")
155
  break
156
 
157
- if max_pairs and len(results) >= max_pairs:
158
  break
159
 
160
- _log(logs, f"DONE Phase A. templates={len(results)} pairs_scanned={pairs_scanned}")
161
 
162
  fd, path = tempfile.mkstemp(prefix="phase_a_", suffix=".json")
163
  os.close(fd)
@@ -172,7 +231,6 @@ def run_phase_a(
172
  indent=2,
173
  )
174
  download_path = path
175
-
176
  yield flush()
177
 
178
  except Exception as e:
@@ -180,31 +238,39 @@ def run_phase_a(
180
  yield "\n".join(logs), "[]", None
181
 
182
 
183
- with gr.Blocks(title="Printify Phase A") as demo:
184
- gr.Markdown("Phase A: discover shop + enumerate blueprint/provider templates (variants + placeholders) and download JSON.")
 
 
 
 
 
 
 
 
 
185
 
186
- shops_log = gr.Textbox(label="Shop Discovery Logs", lines=6)
187
- shop_dropdown = gr.Dropdown(label="Select Shop (pick Atheria)", choices=[], value=None)
188
 
 
 
189
  fetch_shops_btn = gr.Button("Fetch Shops")
190
 
191
- max_pairs = gr.Number(label="Max templates to collect (0 = no limit)", value=25, precision=0)
192
  per_page = gr.Number(label="Blueprints per page", value=100, precision=0)
193
 
194
  run_btn = gr.Button("Run Phase A")
 
195
  logs = gr.Textbox(label="Logs", lines=16)
196
  preview = gr.Textbox(label="Preview (first 3 templates)", lines=16)
197
- download = gr.File(label="Download Phase A JSON")
198
-
199
- def _fetch_shops():
200
- l, choices = list_shops()
201
- return l, gr.Dropdown(choices=choices, value=None)
202
 
203
- fetch_shops_btn.click(_fetch_shops, inputs=[], outputs=[shops_log, shop_dropdown])
204
 
205
  run_btn.click(
206
  run_phase_a,
207
- inputs=[shop_dropdown, max_pairs, per_page],
208
  outputs=[logs, preview, download],
209
  )
210
 
 
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
9
 
10
  PRINTIFY_BASE = "https://api.printify.com"
11
+ DEFAULT_BASE_PRICE = 24.99
12
 
13
 
14
  def _now() -> str:
15
  return time.strftime("%H:%M:%S")
16
 
17
 
18
+ def _sleep(ms: int):
19
+ time.sleep(ms / 1000)
20
+
21
+
22
  def _auth_headers() -> Dict[str, str]:
23
  token = os.environ.get("PRINTIFY_API_TOKEN")
24
  if not token:
25
  raise RuntimeError("Missing PRINTIFY_API_TOKEN (HF Space Secret).")
26
+ return {"Authorization": f"Bearer {token}"}
 
 
 
27
 
28
 
29
  def _req(method: str, path: str) -> Any:
 
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]]:
 
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] = []
 
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
+ shop_choices = []
123
+ for s in shops:
124
+ sid = s.get("id")
125
+ title = s.get("title") or "untitled"
126
+ chan = s.get("sales_channel") or "unknown"
127
+ shop_choices.append((f"{title} ({chan}) — {sid}", str(sid)))
128
 
129
+ if selected_shop_id:
130
+ shop_id = str(selected_shop_id)
131
+ else:
132
+ raise RuntimeError("Pick a shop (Atheria) first, then Run.")
133
+
134
+ _log(logs, f"Using shop_id={shop_id}")
135
  yield flush()
136
 
137
+ max_templates = int(max_templates or 0)
138
+ per_page = int(per_page or 100)
139
+
140
  blueprints = _paginate_blueprints(logs, per_page=per_page)
141
  yield flush()
142
 
143
  pairs_scanned = 0
144
 
145
  for bp in blueprints:
146
+ bp_id = bp.get("id")
147
+ if bp_id is None:
148
  continue
149
+ bp_id_s = str(bp_id)
150
 
151
+ _log(logs, f"Blueprint {bp_id_s}: fetching providers")
152
+ providers = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}/print_providers.json")
153
  if not isinstance(providers, list) or not providers:
154
  continue
155
 
156
  for p in providers:
157
+ p_id = p.get("id")
158
+ if p_id is None:
159
  continue
160
+ p_id_s = str(p_id)
161
 
162
  pairs_scanned += 1
163
+ _log(logs, f"Blueprint {bp_id_s} / Provider {p_id_s}: fetching variants")
164
 
165
+ vr = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}/print_providers/{p_id_s}/variants.json?show-out-of-stock=1")
166
  variants = vr.get("variants") if isinstance(vr, dict) else None
167
  if not isinstance(variants, list) or not variants:
168
  continue
169
 
170
+ _log(logs, f"FOUND {len(variants)} variants (bp={bp_id_s}, provider={p_id_s})")
171
+
172
+ blueprint_details = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}.json")
173
+ provider_details = _req("GET", f"/v1/catalog/print_providers/{p_id_s}.json")
174
+ shipping_info = _req("GET", f"/v1/catalog/blueprints/{bp_id_s}/print_providers/{p_id_s}/shipping.json")
175
+
176
+ variant_snapshot, min_price = _extract_variant_snapshot(variants)
177
+
178
+ _log(logs, "VARIANT_SNAPSHOT " + json.dumps({"count": len(variant_snapshot), "sample": variant_snapshot[:20]}))
179
+
180
+ provider_loc = provider_details.get("location") or {}
181
+ provider_name = provider_details.get("title") or provider_details.get("name") or "empty"
182
+ loc_str = ", ".join([x for x in [provider_loc.get("city"), provider_loc.get("country")] if x]) or "empty"
183
+ base_desc = blueprint_details.get("description") or bp.get("description") or "empty"
184
+ description = f"{base_desc}\n\nFulfilled by {provider_name} — {loc_str}"
185
+
186
+ colors = sorted({v.get("color") for v in variant_snapshot if v.get("color")})
187
+ sizes = sorted({v.get("size") for v in variant_snapshot if v.get("size")})
188
+
189
+ product_template = {
190
+ "shop_id": int(shop_id),
191
+ "blueprint_id": int(bp_id_s),
192
+ "print_provider_id": int(p_id_s),
193
+ "name": blueprint_details.get("title") or bp.get("title") or "empty",
194
+ "description": description,
195
+ "tags": ["Printify", f"Provider: {provider_name}"],
196
+ "options": {"Color": colors, "Size": sizes},
197
+ "variants_count": len(variant_snapshot),
198
+ "price_min": min_price,
199
  "variants": variant_snapshot,
200
  "shipping": shipping_info,
201
+ "raw": {
202
+ "blueprint": bp,
203
+ "provider": p,
204
+ "blueprintDetails": blueprint_details,
205
+ "providerDetails": provider_details,
206
+ },
207
+ }
208
+
209
+ results.append(product_template)
210
  yield flush()
211
 
212
+ if max_templates and len(results) >= max_templates:
213
+ _log(logs, f"Reached max_templates={max_templates}. Stopping.")
214
  break
215
 
216
+ if max_templates and len(results) >= max_templates:
217
  break
218
 
219
+ _log(logs, f"DONE templates={len(results)} pairs_scanned={pairs_scanned}")
220
 
221
  fd, path = tempfile.mkstemp(prefix="phase_a_", suffix=".json")
222
  os.close(fd)
 
231
  indent=2,
232
  )
233
  download_path = path
 
234
  yield flush()
235
 
236
  except Exception as e:
 
238
  yield "\n".join(logs), "[]", None
239
 
240
 
241
+ def fetch_shops_for_dropdown() -> Tuple[str, gr.Dropdown]:
242
+ logs: List[str] = []
243
+ shops = _list_shops(logs)
244
+ choices = []
245
+ for s in shops:
246
+ sid = s.get("id")
247
+ title = s.get("title") or "untitled"
248
+ chan = s.get("sales_channel") or "unknown"
249
+ choices.append((f"{title} ({chan}) — {sid}", str(sid)))
250
+ return "\n".join(logs), gr.Dropdown(choices=choices, value=None)
251
+
252
 
253
+ with gr.Blocks(title="Printify Phase A Probe") as demo:
254
+ gr.Markdown("Phase A: fetch shop ID (pick Atheria) + enumerate blueprint/provider templates (variants + placeholders) + download JSON.")
255
 
256
+ shops_log = gr.Textbox(label="Shop Logs", lines=6)
257
+ shop_dropdown = gr.Dropdown(label="Select Shop (pick Atheria)", choices=[], value=None)
258
  fetch_shops_btn = gr.Button("Fetch Shops")
259
 
260
+ max_templates = gr.Number(label="Max templates (0 = no limit)", value=25, precision=0)
261
  per_page = gr.Number(label="Blueprints per page", value=100, precision=0)
262
 
263
  run_btn = gr.Button("Run Phase A")
264
+
265
  logs = gr.Textbox(label="Logs", lines=16)
266
  preview = gr.Textbox(label="Preview (first 3 templates)", lines=16)
267
+ download = gr.File(label="Download Full Phase A JSON")
 
 
 
 
268
 
269
+ fetch_shops_btn.click(fetch_shops_for_dropdown, inputs=[], outputs=[shops_log, shop_dropdown])
270
 
271
  run_btn.click(
272
  run_phase_a,
273
+ inputs=[shop_dropdown, max_templates, per_page],
274
  outputs=[logs, preview, download],
275
  )
276