Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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":
|
| 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 =
|
| 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=
|
| 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 |
-
|
| 1114 |
-
|
| 1115 |
-
|
| 1116 |
-
|
| 1117 |
-
|
| 1118 |
-
continue
|
| 1119 |
-
try:
|
| 1120 |
-
enabled_ids.add(int(vid))
|
| 1121 |
-
except Exception:
|
| 1122 |
-
continue
|
| 1123 |
|
| 1124 |
_log(
|
| 1125 |
logs,
|
| 1126 |
-
f"
|
| 1127 |
f"chunk_index={chunk_index} total_chunks={total_chunks} "
|
| 1128 |
-
f"offset={offset} size={len(chunk)} total_variants={total_variants}
|
| 1129 |
)
|
| 1130 |
yield flush()
|
| 1131 |
|
| 1132 |
-
|
| 1133 |
-
yield flush()
|
| 1134 |
-
|
| 1135 |
-
upd_stitch = _update_product_stitch_cumulative_with_grid(
|
| 1136 |
logs,
|
| 1137 |
shop_id=shop_id,
|
| 1138 |
-
|
| 1139 |
-
|
| 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 |
-
|
| 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(
|
| 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(
|
| 1167 |
-
"shopVariantCountFinal": len(
|
| 1168 |
"productId": created_id,
|
| 1169 |
-
"priceUpdateResponse":
|
| 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()
|