Theflame47 commited on
Commit
386d787
·
verified ·
1 Parent(s): 8551941

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -328
app.py CHANGED
@@ -8,7 +8,6 @@ import requests
8
  import gradio as gr
9
  import base64
10
  import re
11
- import hashlib
12
 
13
  PRINTIFY_BASE = "https://api.printify.com"
14
  DEFAULT_BASE_PRICE = 0.001
@@ -33,18 +32,6 @@ def _auth_headers() -> Dict[str, str]:
33
  return {"Authorization": f"Bearer {token}"}
34
 
35
 
36
- def _batch_fingerprint(shop_id: str, blueprint_id: int, provider_id: int, enabled_variant_ids: List[int]) -> str:
37
- ids = sorted(int(x) for x in enabled_variant_ids)
38
- payload = {
39
- "shop_id": str(shop_id),
40
- "blueprint_id": int(blueprint_id),
41
- "provider_id": int(provider_id),
42
- "enabled_variant_ids": ids,
43
- }
44
- raw = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")
45
- return hashlib.sha1(raw).hexdigest()
46
-
47
-
48
  def _parse_retry_after_seconds(r: requests.Response) -> Optional[float]:
49
  ra = r.headers.get("Retry-After")
50
  if ra:
@@ -78,15 +65,9 @@ def _req(
78
  path: str,
79
  json_body: Optional[Dict[str, Any]] = None,
80
  logs: Optional[List[str]] = None,
81
- extra_headers: Optional[Dict[str, str]] = None,
82
  ) -> Any:
83
- headers = _auth_headers()
84
- if extra_headers:
85
- for k, v in extra_headers.items():
86
- headers[str(k)] = str(v)
87
-
88
  kwargs: Dict[str, Any] = {
89
- "headers": headers,
90
  "timeout": 60,
91
  }
92
  if json_body is not None:
@@ -448,49 +429,6 @@ def _log_catalog_variants(logs: List[str], product_info: Dict[str, Any], limit:
448
  break
449
 
450
 
451
- def _find_existing_product_by_fp(
452
- logs: List[str],
453
- shop_id: str,
454
- fp: str,
455
- ) -> Optional[Dict[str, Any]]:
456
- page = 1
457
- while True:
458
- resp = _req(
459
- "GET",
460
- f"/v1/shops/{shop_id}/products.json?page={page}&limit=50",
461
- logs=logs,
462
- )
463
- if isinstance(resp, dict):
464
- items = resp.get("data") or resp.get("products") or resp.get("items")
465
- if isinstance(items, list):
466
- products = items
467
- else:
468
- products = []
469
- elif isinstance(resp, list):
470
- products = resp
471
- else:
472
- products = []
473
-
474
- if not products:
475
- break
476
-
477
- for p in products:
478
- if not isinstance(p, dict):
479
- continue
480
- desc = p.get("description") or ""
481
- if isinstance(desc, str) and f"BATCH_FP={fp}" in desc:
482
- _log(
483
- logs,
484
- f"FOUND_EXISTING_PRODUCT_FOR_FP shop_id={shop_id} product_id={p.get('id')} batch_fp={fp}",
485
- )
486
- return p
487
-
488
- page += 1
489
-
490
- _log(logs, f"NO_EXISTING_PRODUCT_FOR_FP shop_id={shop_id} batch_fp={fp}")
491
- return None
492
-
493
-
494
  def _create_product_all_variants_with_grid(
495
  logs: List[str],
496
  shop_id: str,
@@ -528,29 +466,6 @@ def _create_product_all_variants_with_grid(
528
  except Exception:
529
  continue
530
 
531
- all_ids_for_fp = []
532
- for v in all_variants:
533
- if not isinstance(v, dict):
534
- continue
535
- vid = v.get("id")
536
- if vid is None:
537
- continue
538
- try:
539
- all_ids_for_fp.append(int(vid))
540
- except Exception:
541
- continue
542
-
543
- fp = _batch_fingerprint(str(shop_id), int(bp_id), int(provider_id), sorted(all_ids_for_fp))
544
-
545
- existing = _find_existing_product_by_fp(logs, shop_id, fp)
546
- if isinstance(existing, dict) and existing.get("id"):
547
- _log(
548
- logs,
549
- f"PHASE_B_REUSE_EXISTING_PRODUCT shop_id={shop_id} blueprint_id={bp_id} "
550
- f"provider_id={provider_id} existing_product_id={existing.get('id')} batch_fp={fp}",
551
- )
552
- return existing
553
-
554
  img_id = upload.get("id")
555
  img_w = float(upload.get("width") or 0)
556
  img_h = float(upload.get("height") or 0)
@@ -636,7 +551,7 @@ def _create_product_all_variants_with_grid(
636
  provider_name = provider_details.get("title") or provider_details.get("name") or str(provider_id)
637
 
638
  title = (product_info.get("name") or "Printify Grid Test") + f" — {provider_name}"
639
- description = (product_info.get("description") or "") + f"\n\nBATCH_FP={fp}"
640
 
641
  payload = {
642
  "title": title,
@@ -650,8 +565,7 @@ def _create_product_all_variants_with_grid(
650
  _log(
651
  logs,
652
  f"PHASE_B_CREATE shop_id={shop_id} blueprint_id={bp_id} provider_id={provider_id} "
653
- f"variants_payload_total={len(variants_payload)} enabled={enabled_count} print_areas_payload={len(print_areas_payload)} "
654
- f"idempotency_key={fp}",
655
  )
656
 
657
  created = _req(
@@ -667,126 +581,6 @@ def _create_product_all_variants_with_grid(
667
  return created
668
 
669
 
670
- def _update_product_stitch_cumulative_with_grid(
671
- logs: List[str],
672
- shop_id: str,
673
- product_id: str,
674
- all_variants: List[Dict[str, Any]],
675
- enabled_ids: set,
676
- upload: Dict[str, Any],
677
- title: Optional[str],
678
- description: Optional[str],
679
- ) -> Dict[str, Any]:
680
- img_id = upload.get("id")
681
- img_w = float(upload.get("width") or 0)
682
- img_h = float(upload.get("height") or 0)
683
-
684
- variants_payload = []
685
- print_areas_payload = []
686
-
687
- enabled_count = 0
688
- for v in all_variants:
689
- if not isinstance(v, dict):
690
- continue
691
- vid = v.get("id")
692
- if vid is None:
693
- continue
694
- try:
695
- vid_i = int(vid)
696
- except Exception:
697
- continue
698
- is_on = vid_i in enabled_ids
699
- if is_on:
700
- enabled_count += 1
701
- variants_payload.append({
702
- "id": vid_i,
703
- "price": 1,
704
- "is_enabled": bool(is_on),
705
- })
706
-
707
- for v in all_variants:
708
- if not isinstance(v, dict):
709
- continue
710
- vid = v.get("id")
711
- if vid is None:
712
- continue
713
- try:
714
- vid_i = int(vid)
715
- except Exception:
716
- continue
717
- if vid_i not in enabled_ids:
718
- continue
719
-
720
- placeholders = v.get("placeholders") or []
721
- if not isinstance(placeholders, list) or not placeholders:
722
- continue
723
-
724
- ph_payload = []
725
- for ph in placeholders:
726
- if not isinstance(ph, dict):
727
- continue
728
- pos = ph.get("position")
729
- pw = ph.get("width")
730
- phh = ph.get("height")
731
- if not pos or pw is None or phh is None:
732
- continue
733
- try:
734
- pwf = float(pw)
735
- phf = float(phh)
736
- except Exception:
737
- continue
738
- scale = _scale_fill(pwf, phf, img_w, img_h)
739
- ph_payload.append({
740
- "position": pos,
741
- "images": [{
742
- "id": img_id,
743
- "x": 0.5,
744
- "y": 0.5,
745
- "scale": scale,
746
- "angle": 0,
747
- }],
748
- })
749
-
750
- if ph_payload:
751
- print_areas_payload.append({
752
- "variant_ids": [vid_i],
753
- "placeholders": ph_payload,
754
- })
755
-
756
- if not variants_payload:
757
- raise RuntimeError("No variants payload could be built for stitch update.")
758
- if enabled_count > 0 and not print_areas_payload:
759
- raise RuntimeError("Enabled variants exist but no print_areas payload could be generated (stitch update).")
760
-
761
- payload: Dict[str, Any] = {
762
- "variants": variants_payload,
763
- "print_areas": print_areas_payload,
764
- }
765
- if isinstance(title, str) and title:
766
- payload["title"] = title
767
- if isinstance(description, str) and description:
768
- payload["description"] = description
769
-
770
- _log(
771
- logs,
772
- f"PHASE_B_STITCH_UPDATE product_id={product_id} "
773
- f"variants_payload_total={len(variants_payload)} enabled={enabled_count} "
774
- f"print_areas_payload={len(print_areas_payload)}",
775
- )
776
-
777
- upd = _req(
778
- "PUT",
779
- f"/v1/shops/{shop_id}/products/{product_id}.json",
780
- json_body=payload,
781
- logs=logs,
782
- )
783
- if not isinstance(upd, dict):
784
- raise RuntimeError(f"Unexpected update response: {str(upd)[:500]}")
785
-
786
- _log(logs, f"PHASE_B_STITCH_UPDATE_DONE product_id={product_id}")
787
- return upd
788
-
789
-
790
  def _get_product(logs: List[str], shop_id: str, product_id: str) -> Dict[str, Any]:
791
  prod = _req("GET", f"/v1/shops/{shop_id}/products/{product_id}.json", logs=logs)
792
  if not isinstance(prod, dict):
@@ -853,16 +647,10 @@ def _update_prices_to_cost_plus_margin(
853
  if not payload_variants:
854
  raise RuntimeError("No variant prices could be computed from costs.")
855
 
856
- upd_body: Dict[str, Any] = {"variants": payload_variants}
857
- if isinstance(product.get("title"), str):
858
- upd_body["title"] = product.get("title")
859
- if isinstance(product.get("description"), str):
860
- upd_body["description"] = product.get("description")
861
-
862
  upd = _req(
863
  "PUT",
864
  f"/v1/shops/{shop_id}/products/{product_id}.json",
865
- json_body=upd_body,
866
  logs=logs,
867
  )
868
  if not isinstance(upd, dict):
@@ -1045,116 +833,53 @@ def phase_b(currency: str) -> Generator[Tuple[str, str], None, None]:
1045
 
1046
  else:
1047
  total_chunks = (total_variants + CHUNK_TARGET_VARIANTS - 1) // CHUNK_TARGET_VARIANTS
1048
-
1049
- enabled_ids: set = set()
1050
  chunk_index = 0
1051
-
1052
- first_chunk = variants_all[0:CHUNK_TARGET_VARIANTS]
1053
- for v in first_chunk:
1054
- if not isinstance(v, dict):
1055
- continue
1056
- vid = v.get("id")
1057
- if vid is None:
1058
- continue
1059
- try:
1060
- enabled_ids.add(int(vid))
1061
- except Exception:
1062
- continue
1063
-
1064
- p_info_first = dict(product_info_full)
1065
- p_info_first["variants"] = first_chunk
1066
- p_info_first["options"] = _rebuild_options_for_variants(first_chunk)
1067
- p_info_first["_allVariants"] = variants_all
1068
- p_info_first["_enabledVariantIds"] = sorted(list(enabled_ids))
1069
-
1070
- _log(
1071
- logs,
1072
- f"PROVIDER_CHUNK_CREATE provider_id={provider_id} name={provider_name} "
1073
- f"chunk_index={chunk_index} total_chunks={total_chunks} "
1074
- f"offset={0} size={len(first_chunk)} total_variants={total_variants}",
1075
- )
1076
- yield flush()
1077
-
1078
- created = _create_product_all_variants_with_grid(
1079
- logs,
1080
- shop_id=shop_id,
1081
- blob=blob,
1082
- product_info=p_info_first,
1083
- upload=upload,
1084
- )
1085
- created_id = str(created.get("id") or "")
1086
- if not created_id:
1087
- raise RuntimeError("Created product response missing id.")
1088
- yield flush()
1089
-
1090
- prod0 = _get_product(logs, shop_id, created_id)
1091
- yield flush()
1092
-
1093
- provider_runs.append(
1094
- {
1095
- "providerId": provider_id,
1096
- "providerName": provider_name,
1097
- "chunkIndex": chunk_index,
1098
- "totalChunks": total_chunks,
1099
- "catalogVariantCountTotal": total_variants,
1100
- "catalogVariantCountChunk": len(first_chunk),
1101
- "shopVariantCountAfterCreate": len(prod0.get("variants") or []),
1102
- "shopVariantCountFinal": len(prod0.get("variants") or []),
1103
- "productId": created_id,
1104
- "priceUpdateResponse": {},
1105
- }
1106
- )
1107
-
1108
- offset = CHUNK_TARGET_VARIANTS
1109
- chunk_index = 1
1110
-
1111
  while offset < total_variants:
1112
  chunk = variants_all[offset: offset + CHUNK_TARGET_VARIANTS]
1113
- for v in chunk:
1114
- if not isinstance(v, dict):
1115
- continue
1116
- vid = v.get("id")
1117
- if vid is None:
1118
- continue
1119
- try:
1120
- enabled_ids.add(int(vid))
1121
- except Exception:
1122
- continue
1123
 
1124
  _log(
1125
  logs,
1126
- f"PROVIDER_CHUNK_STITCH provider_id={provider_id} name={provider_name} "
1127
  f"chunk_index={chunk_index} total_chunks={total_chunks} "
1128
- f"offset={offset} size={len(chunk)} total_variants={total_variants} product_id={created_id}",
1129
  )
1130
  yield flush()
1131
 
1132
- current_prod = _get_product(logs, shop_id, created_id)
1133
- yield flush()
1134
-
1135
- upd_stitch = _update_product_stitch_cumulative_with_grid(
1136
  logs,
1137
  shop_id=shop_id,
1138
- product_id=created_id,
1139
- all_variants=variants_all,
1140
- enabled_ids=enabled_ids,
1141
  upload=upload,
1142
- title=current_prod.get("title"),
1143
- description=current_prod.get("description"),
1144
  )
 
 
 
1145
  yield flush()
1146
 
1147
- prod_u = _get_product(logs, shop_id, created_id)
1148
  yield flush()
1149
 
1150
  _log(
1151
  logs,
1152
  f"COMPARE_COUNTS provider={provider_id} chunk_index={chunk_index} "
1153
  f"catalog_chunk={len(chunk)} "
1154
- f"shop={len(prod_u.get('variants') or [])}",
1155
  )
1156
  yield flush()
1157
 
 
 
 
 
 
 
1158
  provider_runs.append(
1159
  {
1160
  "providerId": provider_id,
@@ -1163,40 +888,16 @@ def phase_b(currency: str) -> Generator[Tuple[str, str], None, None]:
1163
  "totalChunks": total_chunks,
1164
  "catalogVariantCountTotal": total_variants,
1165
  "catalogVariantCountChunk": len(chunk),
1166
- "shopVariantCountAfterCreate": len(prod_u.get("variants") or []),
1167
- "shopVariantCountFinal": len(prod_u.get("variants") or []),
1168
  "productId": created_id,
1169
- "priceUpdateResponse": upd_stitch,
1170
  }
1171
  )
1172
 
1173
  offset += CHUNK_TARGET_VARIANTS
1174
  chunk_index += 1
1175
 
1176
- prod_before_price = _get_product(logs, shop_id, created_id)
1177
- yield flush()
1178
-
1179
- upd_prices = _update_prices_to_cost_plus_margin(logs, shop_id, created_id, prod_before_price)
1180
- yield flush()
1181
-
1182
- prod_after_price = _get_product(logs, shop_id, created_id)
1183
- yield flush()
1184
-
1185
- provider_runs.append(
1186
- {
1187
- "providerId": provider_id,
1188
- "providerName": provider_name,
1189
- "chunkIndex": "PRICE_UPDATE",
1190
- "totalChunks": total_chunks,
1191
- "catalogVariantCountTotal": total_variants,
1192
- "catalogVariantCountChunk": 0,
1193
- "shopVariantCountAfterCreate": len(prod_before_price.get("variants") or []),
1194
- "shopVariantCountFinal": len(prod_after_price.get("variants") or []),
1195
- "productId": created_id,
1196
- "priceUpdateResponse": upd_prices,
1197
- }
1198
- )
1199
-
1200
  result["providerRuns"] = provider_runs
1201
  _log(logs, "PHASE_B_DONE")
1202
  yield flush()
 
8
  import gradio as gr
9
  import base64
10
  import re
 
11
 
12
  PRINTIFY_BASE = "https://api.printify.com"
13
  DEFAULT_BASE_PRICE = 0.001
 
32
  return {"Authorization": f"Bearer {token}"}
33
 
34
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  def _parse_retry_after_seconds(r: requests.Response) -> Optional[float]:
36
  ra = r.headers.get("Retry-After")
37
  if ra:
 
65
  path: str,
66
  json_body: Optional[Dict[str, Any]] = None,
67
  logs: Optional[List[str]] = None,
 
68
  ) -> Any:
 
 
 
 
 
69
  kwargs: Dict[str, Any] = {
70
+ "headers": _auth_headers(),
71
  "timeout": 60,
72
  }
73
  if json_body is not None:
 
429
  break
430
 
431
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  def _create_product_all_variants_with_grid(
433
  logs: List[str],
434
  shop_id: str,
 
466
  except Exception:
467
  continue
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  img_id = upload.get("id")
470
  img_w = float(upload.get("width") or 0)
471
  img_h = float(upload.get("height") or 0)
 
551
  provider_name = provider_details.get("title") or provider_details.get("name") or str(provider_id)
552
 
553
  title = (product_info.get("name") or "Printify Grid Test") + f" — {provider_name}"
554
+ description = product_info.get("description") or ""
555
 
556
  payload = {
557
  "title": title,
 
565
  _log(
566
  logs,
567
  f"PHASE_B_CREATE shop_id={shop_id} blueprint_id={bp_id} provider_id={provider_id} "
568
+ f"variants_payload_total={len(variants_payload)} enabled={enabled_count} print_areas_payload={len(print_areas_payload)}",
 
569
  )
570
 
571
  created = _req(
 
581
  return created
582
 
583
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
  def _get_product(logs: List[str], shop_id: str, product_id: str) -> Dict[str, Any]:
585
  prod = _req("GET", f"/v1/shops/{shop_id}/products/{product_id}.json", logs=logs)
586
  if not isinstance(prod, dict):
 
647
  if not payload_variants:
648
  raise RuntimeError("No variant prices could be computed from costs.")
649
 
 
 
 
 
 
 
650
  upd = _req(
651
  "PUT",
652
  f"/v1/shops/{shop_id}/products/{product_id}.json",
653
+ json_body={"variants": payload_variants},
654
  logs=logs,
655
  )
656
  if not isinstance(upd, dict):
 
833
 
834
  else:
835
  total_chunks = (total_variants + CHUNK_TARGET_VARIANTS - 1) // CHUNK_TARGET_VARIANTS
 
 
836
  chunk_index = 0
837
+ offset = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
  while offset < total_variants:
839
  chunk = variants_all[offset: offset + CHUNK_TARGET_VARIANTS]
840
+ p_info_chunk = dict(product_info_full)
841
+ p_info_chunk["variants"] = chunk
842
+ p_info_chunk["options"] = _rebuild_options_for_variants(chunk)
843
+ p_info_chunk["_allVariants"] = variants_all
844
+ p_info_chunk["_enabledVariantIds"] = [int(v.get("id")) for v in chunk if v.get("id") is not None]
 
 
 
 
 
845
 
846
  _log(
847
  logs,
848
+ f"PROVIDER_CHUNK provider_id={provider_id} name={provider_name} "
849
  f"chunk_index={chunk_index} total_chunks={total_chunks} "
850
+ f"offset={offset} size={len(chunk)} total_variants={total_variants}",
851
  )
852
  yield flush()
853
 
854
+ created = _create_product_all_variants_with_grid(
 
 
 
855
  logs,
856
  shop_id=shop_id,
857
+ blob=blob,
858
+ product_info=p_info_chunk,
 
859
  upload=upload,
 
 
860
  )
861
+ created_id = str(created.get("id") or "")
862
+ if not created_id:
863
+ raise RuntimeError("Created product response missing id.")
864
  yield flush()
865
 
866
+ prod1 = _get_product(logs, shop_id, created_id)
867
  yield flush()
868
 
869
  _log(
870
  logs,
871
  f"COMPARE_COUNTS provider={provider_id} chunk_index={chunk_index} "
872
  f"catalog_chunk={len(chunk)} "
873
+ f"shop={len(prod1.get('variants') or [])}",
874
  )
875
  yield flush()
876
 
877
+ upd = _update_prices_to_cost_plus_margin(logs, shop_id, created_id, prod1)
878
+ yield flush()
879
+
880
+ prod2 = _get_product(logs, shop_id, created_id)
881
+ yield flush()
882
+
883
  provider_runs.append(
884
  {
885
  "providerId": provider_id,
 
888
  "totalChunks": total_chunks,
889
  "catalogVariantCountTotal": total_variants,
890
  "catalogVariantCountChunk": len(chunk),
891
+ "shopVariantCountAfterCreate": len(prod1.get("variants") or []),
892
+ "shopVariantCountFinal": len(prod2.get("variants") or []),
893
  "productId": created_id,
894
+ "priceUpdateResponse": upd,
895
  }
896
  )
897
 
898
  offset += CHUNK_TARGET_VARIANTS
899
  chunk_index += 1
900
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
  result["providerRuns"] = provider_runs
902
  _log(logs, "PHASE_B_DONE")
903
  yield flush()