feat: add rows_processed/rows_skipped diagnostics to health + refresh endpoints
Helps diagnose whether the product cap is from EAN filtering or a downstream limit. health and refresh now return: product_count, rows_processed, rows_skipped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
39
main.py
39
main.py
@@ -39,8 +39,12 @@ def safe_float(value, default):
|
|||||||
return float(str(value).strip())
|
return float(str(value).strip())
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
products_cache = []
|
products_cache = []
|
||||||
last_refresh = None
|
last_refresh = None
|
||||||
|
rows_processed = 0
|
||||||
|
rows_skipped = 0
|
||||||
cache_lock = threading.Lock()
|
cache_lock = threading.Lock()
|
||||||
api_key_header = APIKeyHeader(name="X-API-Key")
|
api_key_header = APIKeyHeader(name="X-API-Key")
|
||||||
|
|
||||||
@@ -52,18 +56,19 @@ def verify_key(key: str = Security(api_key_header)):
|
|||||||
|
|
||||||
|
|
||||||
def download_and_parse():
|
def download_and_parse():
|
||||||
global products_cache, last_refresh
|
global products_cache, last_refresh, rows_processed, rows_skipped
|
||||||
resp = requests.get(EXCEL_URL, timeout=60)
|
resp = requests.get(EXCEL_URL, timeout=60)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
# read_only=True would stop at the sheet's declared dimension attribute, silently
|
# read_only=True would stop at the sheet's declared <dimension ref> attribute,
|
||||||
# missing any rows MTZ added beyond the original range. Since the file is already
|
# silently missing any rows added beyond the original range.
|
||||||
# in memory (BytesIO), read_only gives no I/O benefit and data_only=True suffices.
|
|
||||||
wb = load_workbook(BytesIO(resp.content), data_only=True)
|
wb = load_workbook(BytesIO(resp.content), data_only=True)
|
||||||
ws = wb.active
|
ws = wb.active
|
||||||
rows = list(ws.iter_rows(min_row=6, values_only=True))
|
rows = list(ws.iter_rows(min_row=6, values_only=True))
|
||||||
parsed = []
|
parsed = []
|
||||||
|
skipped = 0
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row[1] is None: # col B (index 1) = item_code
|
if row[1] is None: # col B (index 1) = item_code — empty row
|
||||||
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
ean_raw = row[3] # col D
|
ean_raw = row[3] # col D
|
||||||
if ean_raw is None:
|
if ean_raw is None:
|
||||||
@@ -75,6 +80,7 @@ def download_and_parse():
|
|||||||
|
|
||||||
# Skip products with blank or non-numeric EAN codes
|
# Skip products with blank or non-numeric EAN codes
|
||||||
if not ean or not ean.isdigit():
|
if not ean or not ean.isdigit():
|
||||||
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
brand_raw = row[9] # col J
|
brand_raw = row[9] # col J
|
||||||
@@ -93,6 +99,8 @@ def download_and_parse():
|
|||||||
)
|
)
|
||||||
with cache_lock:
|
with cache_lock:
|
||||||
products_cache = parsed
|
products_cache = parsed
|
||||||
|
rows_processed = len(rows)
|
||||||
|
rows_skipped = skipped
|
||||||
last_refresh = time.time()
|
last_refresh = time.time()
|
||||||
wb.close()
|
wb.close()
|
||||||
|
|
||||||
@@ -115,11 +123,14 @@ def startup():
|
|||||||
|
|
||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
def health():
|
def health():
|
||||||
return {
|
with cache_lock:
|
||||||
"status": "ok",
|
return {
|
||||||
"product_count": len(products_cache),
|
"status": "ok",
|
||||||
"last_refresh": last_refresh,
|
"product_count": len(products_cache),
|
||||||
}
|
"rows_processed": rows_processed,
|
||||||
|
"rows_skipped": rows_skipped,
|
||||||
|
"last_refresh": last_refresh,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/products", dependencies=[Depends(verify_key)])
|
@app.get("/api/products", dependencies=[Depends(verify_key)])
|
||||||
@@ -131,4 +142,10 @@ def get_products():
|
|||||||
@app.post("/api/refresh", dependencies=[Depends(verify_key)])
|
@app.post("/api/refresh", dependencies=[Depends(verify_key)])
|
||||||
def refresh():
|
def refresh():
|
||||||
download_and_parse()
|
download_and_parse()
|
||||||
return {"status": "ok", "product_count": len(products_cache)}
|
with cache_lock:
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"product_count": len(products_cache),
|
||||||
|
"rows_processed": rows_processed,
|
||||||
|
"rows_skipped": rows_skipped,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user