test glue

This commit is contained in:
C committed 2026-01-04 11:18:43 -08:00
1 parent 03f6e84965
commit d341684567
3 files changed
+411 -38

No files matched your search

+165 -11
View File
@@ -51,10 +51,93 @@ async def get_itm(sku: str):
return {"name": name, "id": itm_id}
async def update_itm(lego_set: dict):
async def get_item_by_id(item_id: str) -> dict:
"""
Retrieve full item details by Square catalog item ID.
Args:
item_id: Square catalog item ID
Returns:
dict: Item details including name, category_id, variations, etc.
Raises:
Exception: If item not found or API error
"""
try:
result = await client.catalog.retrieve_catalog_object(
object_id=item_id, include_related_objects=True
)
result_json = json.loads(result.model_dump_json())
if not result_json.get("object"):
raise ValueError(f"Item {item_id} not found")
obj = result_json["object"]
item_data = obj.get("item_data", {})
return {
"id": obj["id"],
"name": item_data.get("name", ""),
"description": item_data.get("description", ""),
"category_id": item_data.get("category_id"),
"variations": item_data.get("variations", []),
"updated_at": obj.get("updated_at"),
"version": obj.get("version"),
}
except Exception as e:
print(f"[@ERROR] Failed to get item {item_id}: {e}")
raise
async def get_item_by_variation_id(variation_id: str) -> dict:
"""
Retrieve parent item details from a variation ID.
Webhooks send variation IDs (inventory.count.updated), but we need
the parent item to extract the set number from the name.
Args:
variation_id: Square catalog item variation ID
Returns:
dict: Parent item details
Raises:
Exception: If variation/item not found or API error
"""
try:
# Get variation object
result = await client.catalog.retrieve_catalog_object(
object_id=variation_id, include_related_objects=True
)
result_json = json.loads(result.model_dump_json())
if not result_json.get("object"):
raise ValueError(f"Variation {variation_id} not found")
variation_obj = result_json["object"]
# Get parent item ID from variation
item_id = variation_obj.get("item_variation_data", {}).get("item_id")
if not item_id:
raise ValueError(f"No parent item_id found for variation {variation_id}")
# Retrieve parent item
return await get_item_by_id(item_id)
except Exception as e:
print(f"[@ERROR] Failed to get item from variation {variation_id}: {e}")
raise
async def update_itm(item_id: str, lego_set: dict):
"""
Asynchronously updates an item in the Square catalog based on the provided LEGO set information.
Args:
item_id (str): Square catalog item ID to update
lego_set (dict): A dictionary containing LEGO set details from abricksetapi. Expected keys include:
- "set_num": LEGO set number (e.g., "75192")
- "set_name": Official LEGO set name
@@ -69,19 +152,90 @@ async def update_itm(lego_set: dict):
KeyError: If expected keys are missing from the input dictionary.
Exception: For errors during Square API interaction.
"""
try:
set_number = lego_set["set_num"]
set_name = lego_set["set_name"]
set_theme = lego_set["set_theme"]
set_upc = lego_set["set_barcode"]
set_image = lego_set["set_image"]
pw_name = lego_set["pw_name"]
set_adjustedprice = lego_set["set_adjustredprice"]
set_number = lego_set["set_num"]
set_name = lego_set["set_name"]
set_upc = lego_set["set_barcode"]
set_image = lego_set["set_image"]
pw_name = lego_set["pw_name"]
set_adjustedprice = lego_set["set_adjustredprice"]
# Convert price to cents for Square API
price_in_cents = int(set_adjustedprice * 100)
result = await client.catalog.search_items(text_filter=set_number, limit=1)
itm_id = json.loads(result.model_dump_json())["items"][0]["id"]
# Get current item to retrieve variation ID
item = await get_item_by_id(item_id)
variations = item.get("variations", [])
# await sq.catalog.object.upsert()
return True
if not variations:
print(
f"[@WARNING] Item {item_id} has no variations, creating new variation"
)
variation_id = None
else:
variation_id = variations[0]["id"]
# Build upsert body
upsert_body = {
"idempotency_key": str(uuid.uuid4()),
"object": {
"type": "ITEM",
"id": item_id,
"item_data": {
"name": pw_name,
"description": f"{set_name} - {set_theme}",
"variations": [
{
"type": "ITEM_VARIATION",
"id": (
variation_id if variation_id else "#TEMP_VARIATION_ID"
),
"item_variation_data": {
"item_id": item_id,
"name": "Regular",
"pricing_type": "FIXED_PRICING",
"price_money": {
"amount": price_in_cents,
"currency": "USD",
},
"sku": set_upc if set_upc else None,
},
}
],
},
},
}
# Remove None values
if not set_upc:
del upsert_body["object"]["item_data"]["variations"][0][
"item_variation_data"
]["sku"]
print(f"[@DEBUG] Upserting item {item_id} with name: {pw_name}")
# Perform upsert
result = await client.catalog.upsert_catalog_object(body=upsert_body)
result_json = json.loads(result.model_dump_json())
if result_json.get("catalog_object"):
print(f"[@DEBUG] ✓ Successfully updated item {item_id}")
# TODO: Handle image upload separately (Square Images API)
# This requires multipart/form-data upload
return True
else:
print(
f"[@ERROR] Failed to update item {item_id}: No catalog_object in response"
)
return False
except Exception as e:
print(f"[@ERROR] Failed to update item {item_id}: {e}")
import traceback
traceback.print_exc()
return False
async def acat2id(cat_id: str):
+178 -24
View File
@@ -9,33 +9,187 @@ from . import asquare
from . import db
from . import abricksetapi
from . import throttler
Throttler = throttler.GlobalThrottler
LEGO_CAT_ID = get_sq_env.LEGO_CAT_ID
# big glue boy
async def do_review() -> bool:
result = await asquare.acat2id(LEGO_CAT_ID) # type: ignore
for item in result:
if not re.match(r".*\d{5}.*", item["name"]):
missing = []
print(f"{item} is missing its set number!")
#print(f"[@DEBUG] Upserting {item['id']} into REVIEW table")
item_sanitized = {
"sq_object": item["id"],
"set_name": item["name"],
"set_number": None,
"time_added": time
}
missing.append(item_sanitized)
for item in missing:
print(item)
# sq_item = await db.fetch(item["id"])
if item["id"] != sq_item["sq_object"]:
print(f"[@DEBUG] {item['id']} is missing)")
#await db.upsert("MISSING", item_sanitized, "sq_object")
# #print(f"[@DEBUG] Upserted {item['id']} into REVIEW table")
#print(f"[@DEBUG] {item['id']} already in REVIEW table")
return True
async def enrich_single_item(item_id: str, logger=None) -> bool:
"""
Enrich a single Square catalog item with Brickset data.
Flow:
1. Get item details from Square (by item_id)
2. Extract 5-digit set number from name (regex \d{5})
3. Fetch Brickset data (with throttling)
4. Update Square item with enriched data
5. Handle errors (no set number → MISSING table)
Args:
item_id: Square catalog item ID
logger: Optional logger instance for debug output
Returns:
True if successful, False otherwise
"""
try:
if logger:
logger.info(f"Starting enrichment for item {item_id}")
else:
print(f"[@DEBUG] Starting enrichment for item {item_id}")
# Get item details from Square
item = await asquare.get_item_by_id(item_id)
item_name = item.get("name", "")
if logger:
logger.debug(f"Item name: {item_name}")
else:
print(f"[@DEBUG] Item name: {item_name}")
# Extract 5-digit set number
set_numbers = re.findall(r"\d{5}", item_name)
if not set_numbers:
# No set number found - log to MISSING table
if logger:
logger.warning(f"No set number found in item {item_id}: {item_name}")
else:
print(f"[@WARNING] No set number found in item {item_id}: {item_name}")
await db.upsert(
"MISSING",
{
"sq_object": item_id,
"time_added": int(time()),
"set_number": None,
"set_name": item_name,
},
"sq_object",
)
return False
set_number = set_numbers[0]
if logger:
logger.info(f"Extracted set number: {set_number}")
else:
print(f"[@DEBUG] Extracted set number: {set_number}")
# Fetch Brickset data (with throttling)
brickset_client = abricksetapi.AsyncBricksetAPI(debug=True, logger=logger)
lego_set = await brickset_client.throttled_get_set(set_number)
if logger:
logger.info(f"Retrieved Brickset data for {lego_set['pw_name']}")
else:
print(f"[@DEBUG] Retrieved Brickset data for {lego_set['pw_name']}")
# Check if manual price review needed
if lego_set.get("requires_manual_price"):
if logger:
logger.info(f"Item {item_id} requires manual price review (≥$110)")
else:
print(f"[@DEBUG] Item {item_id} requires manual price review")
await db.upsert(
"REVIEW",
{
"sq_object": item_id,
"price": lego_set["set_adjustredprice"],
"date_modified": int(time()),
},
"sq_object",
)
# Update Square item with enriched data
success = await asquare.update_itm(item_id, lego_set)
if success:
if logger:
logger.info(f"✓ Successfully enriched item {item_id}")
else:
print(f"[@DEBUG] ✓ Successfully enriched item {item_id}")
return True
else:
if logger:
logger.error(f"Failed to update Square item {item_id}")
else:
print(f"[@ERROR] Failed to update Square item {item_id}")
return False
except ValueError as e:
if logger:
logger.error(f"Value error enriching item {item_id}: {e}")
else:
print(f"[@ERROR] Value error enriching item {item_id}: {e}")
return False
except Exception as e:
if logger:
logger.error(f"Unexpected error enriching item {item_id}: {e}")
else:
print(f"[@ERROR] Unexpected error enriching item {item_id}: {e}")
import traceback
traceback.print_exc()
return False
async def enrich_bulk(logger=None) -> None:
"""
Enrich all items in the LEGO category (bulk operation).
This is the refactored RUNUPDATE command logic.
Iterates all items in LEGO category and enriches each one.
Args:
logger: Optional logger instance for debug output
"""
try:
if logger:
logger.info("Starting bulk enrichment for LEGO category")
else:
print(f"[@DEBUG] Starting bulk enrichment for LEGO category {LEGO_CAT_ID}")
# Get all items in LEGO category
items = await asquare.acat2id(LEGO_CAT_ID)
if logger:
logger.info(f"Found {len(items)} items in LEGO category")
else:
print(f"[@DEBUG] Found {len(items)} items in LEGO category")
# Enrich each item
successful = 0
failed = 0
for item in items:
item_id = item["id"]
result = await enrich_single_item(item_id, logger=logger)
if result:
successful += 1
else:
failed += 1
if logger:
logger.info(
f"Bulk enrichment complete: {successful} successful, {failed} failed"
)
else:
print(
f"[@DEBUG] Bulk enrichment complete: {successful} successful, {failed} failed"
)
except Exception as e:
if logger:
logger.error(f"Error during bulk enrichment: {e}")
else:
print(f"[@ERROR] Error during bulk enrichment: {e}")
import traceback
traceback.print_exc()
+68 -3
View File
@@ -12,6 +12,8 @@ from square.utils.webhooks_helper import verify_signature
# Configuration
SQUARE_WEBHOOK_SIGNATURE_KEY = sq_env.SQUARE_WEBHOOK_SIGNATURE_KEY
SQUARE_WEBHOOK_URL = sq_env.SQUARE_WEBHOOK_URL
SQUARE_LEGO_CAT_ID = sq_env.LEGO_CAT_ID
# Debug: Print configuration (redact key)
if SQUARE_WEBHOOK_SIGNATURE_KEY:
@@ -233,17 +235,77 @@ async def process_webhook(
inventory_counts = (
data.get("data", {}).get("object", {}).get("inventory_counts", [])
)
# Import here to avoid circular imports
from src import asquare, glue
for count in inventory_counts:
catalog_id = count.get("catalog_object_id")
variation_id = count.get("catalog_object_id")
quantity = count.get("quantity")
state = count.get("state")
print(
f"[PROCESS] Catalog: {catalog_id}, Qty: {quantity}, State: {state}"
f"[PROCESS] Catalog: {variation_id}, Qty: {quantity}, State: {state}"
)
try:
# Get parent item from variation ID
item = await asquare.get_item_by_variation_id(variation_id)
item_id = item.get("id")
item_name = item.get("name", "")
category_id = item.get("category_id")
print(f"[PROCESS] Item: {item_id} - {item_name}")
print(f"[PROCESS] Category: {category_id}")
# Only process LEGO category items
if category_id == SQUARE_LEGO_CAT_ID:
print(
f"[PROCESS] ✓ LEGO item detected - triggering enrichment"
)
# Trigger enrichment in background
asyncio.create_task(glue.enrich_single_item(item_id))
else:
print(f"[PROCESS] ✗ Not a LEGO item - skipping enrichment")
except Exception as e:
print(f"[ERROR] Failed to process variation {variation_id}: {e}")
elif webhook_type == "catalog.version.updated":
print(f"[PROCESS] Processing catalog version update...")
# Add your catalog processing logic here
# Import here to avoid circular imports
from src import asquare, glue
catalog_object = data.get("data", {}).get("object", {})
object_type = catalog_object.get("type")
if object_type == "ITEM":
item_id = catalog_object.get("id")
print(f"[PROCESS] Item updated: {item_id}")
try:
# Get item details to check category
item = await asquare.get_item_by_id(item_id)
item_name = item.get("name", "")
category_id = item.get("category_id")
print(f"[PROCESS] Item: {item_id} - {item_name}")
print(f"[PROCESS] Category: {category_id}")
# Only process LEGO category items
if category_id == SQUARE_LEGO_CAT_ID:
print(
f"[PROCESS] ✓ LEGO item detected - triggering enrichment"
)
# Trigger enrichment in background
asyncio.create_task(glue.enrich_single_item(item_id))
else:
print(f"[PROCESS] ✗ Not a LEGO item - skipping enrichment")
except Exception as e:
print(f"[ERROR] Failed to process item {item_id}: {e}")
else:
print(f"[PROCESS] Object type '{object_type}' - skipping")
else:
print(f"[PROCESS] Webhook type '{webhook_type}' - no handler configured")
@@ -252,6 +314,9 @@ async def process_webhook(
except Exception as e:
print(f"[ERROR] Failed to process webhook {event_id}: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":