Theflame47 commited on
Commit
6cf7c2d
·
verified ·
1 Parent(s): ff1632d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +176 -27
app.py CHANGED
@@ -2,13 +2,14 @@ import os
2
  import time
3
  import json
4
  import tempfile
5
- from typing import Dict, Any, List, Generator, Tuple
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:
@@ -26,12 +27,17 @@ def _auth_headers() -> Dict[str, str]:
26
  return {"Authorization": f"Bearer {token}"}
27
 
28
 
29
- def _req(method: str, path: str) -> Any:
 
 
 
 
 
 
30
  r = requests.request(
31
  method,
32
  f"{PRINTIFY_BASE}{path}",
33
- headers=_auth_headers(),
34
- timeout=60,
35
  )
36
  if r.status_code >= 400:
37
  raise RuntimeError(f"HTTP {r.status_code}: {r.text[:2000]}")
@@ -177,26 +183,157 @@ def _build_product(blob: Dict[str, Any], currency: str, logs: List[str]) -> Dict
177
  }
178
 
179
 
180
- def _log_latest_upload(logs: List[str]):
181
- uploads = _req("GET", "/v1/uploads.json?limit=1")
182
- latest = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- if isinstance(uploads, list) and uploads:
185
- latest = uploads[0]
186
- elif isinstance(uploads, dict):
187
- items = uploads.get("data") or uploads.get("uploads") or uploads.get("items") or []
188
- if isinstance(items, list) and items:
189
- latest = items[0]
190
 
191
- if latest:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  _log(
193
  logs,
194
- f"LATEST_UPLOAD id={latest.get('id')} "
195
- f"file={latest.get('file_name') or latest.get('name')} "
196
- f"w={latest.get('width')} h={latest.get('height')}"
197
  )
198
- else:
199
- _log(logs, "LATEST_UPLOAD none found")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
 
202
  def run(currency: str) -> Generator[Tuple[str, str], None, None]:
@@ -214,10 +351,6 @@ def run(currency: str) -> Generator[Tuple[str, str], None, None]:
214
  _log(logs, f"SHOP_LIST {json.dumps(shops)}")
215
  yield flush()
216
 
217
- # NEW: log newest upload right after shops
218
- _log_latest_upload(logs)
219
- yield flush()
220
-
221
  fd, path = tempfile.mkstemp(prefix="shops_", suffix=".json")
222
  os.close(fd)
223
  with open(path, "w", encoding="utf-8") as f:
@@ -253,15 +386,31 @@ def phase_b(currency: str) -> Generator[Tuple[str, str], None, None]:
253
  _log(logs, f"SHOP_LIST {json.dumps(shops)}")
254
  yield flush()
255
 
256
- # SAME upload logging, but under Phase B
257
- _log_latest_upload(logs)
258
  yield flush()
259
 
260
  blob = _find_first_valid_pair(logs)
261
  yield flush()
262
 
263
- result = _build_product(blob, currency or "USD", logs)
264
- _log(logs, "PHASE_B_DONE_NO_CREATE")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  yield flush()
266
 
267
  except Exception as e:
 
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
+ GRID_FILENAME = "grid.png"
13
 
14
 
15
  def _now() -> str:
 
27
  return {"Authorization": f"Bearer {token}"}
28
 
29
 
30
+ def _req(method: str, path: str, json_body: Optional[Dict[str, Any]] = None) -> Any:
31
+ kwargs: Dict[str, Any] = {
32
+ "headers": _auth_headers(),
33
+ "timeout": 60,
34
+ }
35
+ if json_body is not None:
36
+ kwargs["json"] = json_body
37
  r = requests.request(
38
  method,
39
  f"{PRINTIFY_BASE}{path}",
40
+ **kwargs,
 
41
  )
42
  if r.status_code >= 400:
43
  raise RuntimeError(f"HTTP {r.status_code}: {r.text[:2000]}")
 
183
  }
184
 
185
 
186
+ def _pick_shop_id(shops_resp: Any) -> str:
187
+ if isinstance(shops_resp, list):
188
+ items = shops_resp
189
+ elif isinstance(shops_resp, dict):
190
+ items = shops_resp.get("data") or shops_resp.get("shops") or shops_resp.get("items") or []
191
+ else:
192
+ items = []
193
+
194
+ for s in items:
195
+ if isinstance(s, dict) and s.get("id"):
196
+ return str(s["id"])
197
+ raise RuntimeError("No shops found on /v1/shops.json for this token.")
198
+
199
+
200
+ def _upload_grid_from_file(logs: List[str]) -> Dict[str, Any]:
201
+ path = os.path.join(os.getcwd(), GRID_FILENAME)
202
+ if not os.path.exists(path):
203
+ raise RuntimeError(f"Grid image not found at {path}")
204
+
205
+ _log(logs, f"UPLOADING_GRID path={path}")
206
+
207
+ with open(path, "rb") as f:
208
+ r = requests.post(
209
+ f"{PRINTIFY_BASE}/v1/uploads/images.json",
210
+ headers=_auth_headers(),
211
+ files={"file": (GRID_FILENAME, f, "image/png")},
212
+ timeout=60,
213
+ )
214
+
215
+ if r.status_code >= 400:
216
+ raise RuntimeError(f"UPLOAD_HTTP {r.status_code}: {r.text[:2000]}")
217
 
218
+ resp = r.json()
219
+ if not isinstance(resp, dict) or not resp.get("id"):
220
+ raise RuntimeError(f"Unexpected upload response: {str(resp)[:500]}")
 
 
 
221
 
222
+ _log(
223
+ logs,
224
+ f"GRID_UPLOAD id={resp.get('id')} "
225
+ f"file={resp.get('file_name') or resp.get('name')} "
226
+ f"w={resp.get('width')} h={resp.get('height')}",
227
+ )
228
+ return resp
229
+
230
+
231
+ def _scale_fill(ph_w: float, ph_h: float, img_w: float, img_h: float) -> float:
232
+ if ph_w <= 0 or ph_h <= 0 or img_w <= 0 or img_h <= 0:
233
+ return 1.0
234
+ # width-based scale; bump if height would underfill
235
+ s = (ph_h * img_w) / (ph_w * img_h)
236
+ if s < 1.0:
237
+ s = 1.0
238
+ return float(s)
239
+
240
+
241
+ def _create_one_product_with_grid(
242
+ logs: List[str],
243
+ shop_id: str,
244
+ blob: Dict[str, Any],
245
+ product_info: Dict[str, Any],
246
+ upload: Dict[str, Any],
247
+ currency: str,
248
+ ) -> Dict[str, Any]:
249
+ bp_id = (blob.get("blueprint") or {}).get("id")
250
+ provider_id = (blob.get("provider") or {}).get("id")
251
+ if bp_id is None or provider_id is None:
252
+ raise RuntimeError("Missing blueprint or provider id.")
253
+
254
+ variants = product_info.get("variants") or []
255
+ if not isinstance(variants, list) or not variants:
256
+ raise RuntimeError("No variants to create product with.")
257
+
258
+ v = variants[0]
259
+ variant_id = v.get("id")
260
+ if variant_id is None:
261
+ raise RuntimeError("Variant missing id.")
262
+
263
+ placeholders = v.get("placeholders") or []
264
+ img_id = upload.get("id")
265
+ img_w = float(upload.get("width") or 0)
266
+ img_h = float(upload.get("height") or 0)
267
+
268
+ ph_payload = []
269
+ for ph in placeholders:
270
+ if not isinstance(ph, dict):
271
+ continue
272
+ pos = ph.get("position")
273
+ pw = ph.get("width")
274
+ phh = ph.get("height")
275
+ if not pos or pw is None or phh is None:
276
+ continue
277
+ try:
278
+ pwf = float(pw)
279
+ phf = float(phh)
280
+ except Exception:
281
+ continue
282
+ scale = _scale_fill(pwf, phf, img_w, img_h)
283
+ ph_payload.append({
284
+ "position": pos,
285
+ "images": [{
286
+ "id": img_id,
287
+ "x": 0.5,
288
+ "y": 0.5,
289
+ "scale": scale,
290
+ "angle": 0,
291
+ }],
292
+ })
293
  _log(
294
  logs,
295
+ f"FILL_PLACEHOLDER pos={pos} ph={int(pwf)}x{int(phf)} "
296
+ f"img={int(img_w)}x{int(img_h)} scale={round(scale, 6)}",
 
297
  )
298
+
299
+ if not ph_payload:
300
+ raise RuntimeError("No placeholders for chosen variant.")
301
+
302
+ title = product_info.get("name") or "Printify Grid Test"
303
+ description = product_info.get("description") or ""
304
+
305
+ payload = {
306
+ "title": title,
307
+ "description": description,
308
+ "blueprint_id": int(bp_id),
309
+ "print_provider_id": int(provider_id),
310
+ "variants": [{
311
+ "id": int(variant_id),
312
+ "price": int(product_info.get("price", DEFAULT_BASE_PRICE) * 100),
313
+ "is_enabled": True,
314
+ }],
315
+ "print_areas": [{
316
+ "variant_ids": [int(variant_id)],
317
+ "placeholders": ph_payload,
318
+ }],
319
+ }
320
+
321
+ _log(
322
+ logs,
323
+ f"PHASE_B_CREATE shop_id={shop_id} "
324
+ f"blueprint_id={bp_id} provider_id={provider_id} variant_id={variant_id}",
325
+ )
326
+
327
+ created = _req(
328
+ "POST",
329
+ f"/v1/shops/{shop_id}/products.json",
330
+ json_body=payload,
331
+ )
332
+ if not isinstance(created, Dict):
333
+ raise RuntimeError(f"Unexpected create response: {str(created)[:500]}")
334
+
335
+ _log(logs, f"PHASE_B_CREATED product_id={created.get('id')}")
336
+ return created
337
 
338
 
339
  def run(currency: str) -> Generator[Tuple[str, str], None, None]:
 
351
  _log(logs, f"SHOP_LIST {json.dumps(shops)}")
352
  yield flush()
353
 
 
 
 
 
354
  fd, path = tempfile.mkstemp(prefix="shops_", suffix=".json")
355
  os.close(fd)
356
  with open(path, "w", encoding="utf-8") as f:
 
386
  _log(logs, f"SHOP_LIST {json.dumps(shops)}")
387
  yield flush()
388
 
389
+ shop_id = _pick_shop_id(shops)
390
+ _log(logs, f"SHOP_ID {shop_id}")
391
  yield flush()
392
 
393
  blob = _find_first_valid_pair(logs)
394
  yield flush()
395
 
396
+ product_info = _build_product(blob, currency or "USD", logs)
397
+ result["phaseAProduct"] = product_info
398
+ yield flush()
399
+
400
+ upload = _upload_grid_from_file(logs)
401
+ result["gridUpload"] = upload
402
+ yield flush()
403
+
404
+ created = _create_one_product_with_grid(
405
+ logs,
406
+ shop_id=shop_id,
407
+ blob=blob,
408
+ product_info=product_info,
409
+ upload=upload,
410
+ currency=currency or "USD",
411
+ )
412
+ result["createdProduct"] = created
413
+ _log(logs, "PHASE_B_DONE")
414
  yield flush()
415
 
416
  except Exception as e: