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} 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. Asynchronously updates an item in the Square catalog based on the provided LEGO set information.
Args: Args:
item_id (str): Square catalog item ID to update
lego_set (dict): A dictionary containing LEGO set details from abricksetapi. Expected keys include: lego_set (dict): A dictionary containing LEGO set details from abricksetapi. Expected keys include:
- "set_num": LEGO set number (e.g., "75192") - "set_num": LEGO set number (e.g., "75192")
- "set_name": Official LEGO set name - "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. KeyError: If expected keys are missing from the input dictionary.
Exception: For errors during Square API interaction. 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"] # Convert price to cents for Square API
set_name = lego_set["set_name"] price_in_cents = int(set_adjustedprice * 100)
set_upc = lego_set["set_barcode"]
set_image = lego_set["set_image"]
pw_name = lego_set["pw_name"]
set_adjustedprice = lego_set["set_adjustredprice"]
result = await client.catalog.search_items(text_filter=set_number, limit=1) # Get current item to retrieve variation ID
itm_id = json.loads(result.model_dump_json())["items"][0]["id"] item = await get_item_by_id(item_id)
variations = item.get("variations", [])
# await sq.catalog.object.upsert() if not variations:
return True 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): async def acat2id(cat_id: str):
+178 -24
View File
@@ -9,33 +9,187 @@ from . import asquare
from . import db from . import db
from . import abricksetapi from . import abricksetapi
from . import throttler from . import throttler
Throttler = throttler.GlobalThrottler Throttler = throttler.GlobalThrottler
LEGO_CAT_ID = get_sq_env.LEGO_CAT_ID 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: async def enrich_single_item(item_id: str, logger=None) -> bool:
print(item) """
# sq_item = await db.fetch(item["id"]) Enrich a single Square catalog item with Brickset data.
if item["id"] != sq_item["sq_object"]:
print(f"[@DEBUG] {item['id']} is missing)") Flow:
#await db.upsert("MISSING", item_sanitized, "sq_object") 1. Get item details from Square (by item_id)
# #print(f"[@DEBUG] Upserted {item['id']} into REVIEW table") 2. Extract 5-digit set number from name (regex \d{5})
#print(f"[@DEBUG] {item['id']} already in REVIEW table") 3. Fetch Brickset data (with throttling)
return True 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 # Configuration
SQUARE_WEBHOOK_SIGNATURE_KEY = sq_env.SQUARE_WEBHOOK_SIGNATURE_KEY SQUARE_WEBHOOK_SIGNATURE_KEY = sq_env.SQUARE_WEBHOOK_SIGNATURE_KEY
SQUARE_WEBHOOK_URL = sq_env.SQUARE_WEBHOOK_URL SQUARE_WEBHOOK_URL = sq_env.SQUARE_WEBHOOK_URL
SQUARE_LEGO_CAT_ID = sq_env.LEGO_CAT_ID
# Debug: Print configuration (redact key) # Debug: Print configuration (redact key)
if SQUARE_WEBHOOK_SIGNATURE_KEY: if SQUARE_WEBHOOK_SIGNATURE_KEY:
@@ -233,17 +235,77 @@ async def process_webhook(
inventory_counts = ( inventory_counts = (
data.get("data", {}).get("object", {}).get("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: for count in inventory_counts:
catalog_id = count.get("catalog_object_id") variation_id = count.get("catalog_object_id")
quantity = count.get("quantity") quantity = count.get("quantity")
state = count.get("state") state = count.get("state")
print( 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": elif webhook_type == "catalog.version.updated":
print(f"[PROCESS] Processing catalog version update...") 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: else:
print(f"[PROCESS] Webhook type '{webhook_type}' - no handler configured") print(f"[PROCESS] Webhook type '{webhook_type}' - no handler configured")
@@ -252,6 +314,9 @@ async def process_webhook(
except Exception as e: except Exception as e:
print(f"[ERROR] Failed to process webhook {event_id}: {e}") print(f"[ERROR] Failed to process webhook {event_id}: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__": if __name__ == "__main__":