#!/usr/bin/env python3 """ Test script for Letzshop historical order import. Usage: python scripts/test_historical_import.py YOUR_API_KEY [--max-pages 2] [--debug] This script tests the historical import functionality by: 1. Testing different query variations to find what works 2. Showing what would be imported 3. Debugging GraphQL errors """ import argparse import sys from pathlib import Path import requests # Add project root to path sys.path.insert(0, str(Path(__file__).parent.parent)) # Different query variations to test QUERIES = { "minimal": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number state } } } """, "with_order": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number state order { id number email total locale } } } } """, "with_address": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number state order { id number email shipAddress { firstName lastName city zipCode } } } } } """, "with_country": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number order { id shipAddress { country { iso } } } } } } """, "with_inventory": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number inventoryUnits { id state variant { id sku price } } } } } """, "with_tradeid": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number inventoryUnits { id variant { id sku tradeId { number parser } } } } } } """, "with_product": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number inventoryUnits { id variant { id product { name { en fr de } } } } } } } """, "with_tracking_paginated": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number tracking { code provider } } } } """, "tracking_no_pagination": """ query { shipments(state: confirmed) { nodes { id number tracking { code provider } } } } """, "single_shipment_with_tracking": """ query GetShipment($id: ID!) { node(id: $id) { ... on Shipment { id number tracking { code provider } } } } """, "with_mpn": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number inventoryUnits { id variant { id sku mpn } } } } } """, "with_completedAt": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number order { id completedAt } } } } """, "with_full_address": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number order { id shipAddress { firstName lastName company streetName streetNumber city zipCode phone } } } } } """, "combined_no_tracking": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number state order { id number email total completedAt locale shipAddress { firstName lastName company streetName streetNumber city zipCode phone country { iso } } } inventoryUnits { id state variant { id sku mpn price tradeId { number parser } product { name { en fr de } } } } } } } """, "full": """ query GetShipmentsPaginated($first: Int!, $after: String) { shipments(state: confirmed, first: $first, after: $after) { pageInfo { hasNextPage endCursor } nodes { id number state order { id number email total completedAt locale shipAddress { firstName lastName company streetName streetNumber city zipCode phone country { iso } } } inventoryUnits { id state variant { id sku mpn price tradeId { number parser } product { name { en fr de } } } } tracking { code provider } } } } """, } def test_query(api_key: str, query_name: str, query: str, page_size: int = 5, shipment_id: str = None) -> bool: """Test a single query and return True if it works.""" print(f"\n Testing '{query_name}'... ", end="", flush=True) try: # Check if query uses pagination variables uses_pagination = "$first" in query uses_node_id = "$id" in query payload = {"query": query} if uses_pagination: payload["variables"] = {"first": page_size, "after": None} elif uses_node_id: if not shipment_id: print("SKIPPED - needs shipment ID") return None # Return None to indicate skipped payload["variables"] = {"id": shipment_id} response = requests.post( "https://letzshop.lu/graphql", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json=payload, timeout=30, ) data = response.json() if "errors" in data and data["errors"]: error_msg = data["errors"][0].get("message", "Unknown error") print(f"FAILED - {error_msg[:50]}") return False # Handle different response structures if uses_node_id: node = data.get("data", {}).get("node") if node: tracking = node.get("tracking") print(f"OK - tracking: {tracking}") else: print("OK - no node returned") return True else: shipments = data.get("data", {}).get("shipments", {}).get("nodes", []) print(f"OK - {len(shipments)} shipments") return True except Exception as e: print(f"ERROR - {e}") return False def get_first_shipment_id(api_key: str) -> str | None: """Get the ID of the first confirmed shipment for testing.""" query = """ query { shipments(state: confirmed) { nodes { id } } } """ try: response = requests.post( "https://letzshop.lu/graphql", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={"query": query}, timeout=30, ) data = response.json() nodes = data.get("data", {}).get("shipments", {}).get("nodes", []) if nodes: return nodes[0].get("id") except Exception: pass return None def main(): parser = argparse.ArgumentParser(description="Test Letzshop historical import") parser.add_argument("api_key", help="Letzshop API key") parser.add_argument( "--state", default="confirmed", choices=["confirmed", "unconfirmed"], help="Shipment state to fetch", ) parser.add_argument( "--max-pages", type=int, default=2, help="Maximum pages to fetch (default: 2 for testing)", ) parser.add_argument( "--page-size", type=int, default=10, help="Page size (default: 10 for testing)", ) parser.add_argument( "--debug", action="store_true", help="Run query diagnostics to find problematic fields", ) parser.add_argument( "--query", choices=list(QUERIES.keys()), help="Test a specific query variation", ) args = parser.parse_args() print("=" * 60) print("Letzshop Historical Import Test") print("=" * 60) # Debug mode - test all query variations if args.debug: print("\nRunning query diagnostics...") print("Testing different query variations to find what works:") # Get a shipment ID for single-shipment tests shipment_id = get_first_shipment_id(args.api_key) if shipment_id: print(f"\n Got shipment ID for testing: {shipment_id[:20]}...") results = {} for name, query in QUERIES.items(): results[name] = test_query(args.api_key, name, query, args.page_size, shipment_id) print("\n" + "=" * 60) print("Results Summary:") print("=" * 60) for name, success in results.items(): if success is None: status = "⏭️ SKIPPED" elif success: status = "✅ WORKS" else: status = "❌ FAILS" print(f" {name}: {status}") # Find the last working query working = [name for name, success in results.items() if success is True] failing = [name for name, success in results.items() if success is False] if failing: print(f"\n⚠️ Problem detected!") print(f" Working queries: {', '.join(working) if working else 'none'}") print(f" Failing queries: {', '.join(failing)}") if working: print(f"\n The issue is likely in fields added after '{working[-1]}'") else: print("\n✅ All queries work! The issue may be elsewhere.") return # Test specific query if args.query: query = QUERIES[args.query] print(f"\nTesting query: {args.query}") test_query(args.api_key, args.query, query, args.page_size) return print(f"\nState: {args.state}") print(f"Max pages: {args.max_pages}") print(f"Page size: {args.page_size}") # Progress callback def progress(page, total): print(f" Page {page}: {total} shipments fetched so far") # Import here to avoid issues if just doing debug from app.services.letzshop.client_service import LetzshopClient # Create client and fetch with LetzshopClient(api_key=args.api_key) as client: print(f"\n{'='*60}") print("Fetching shipments with pagination...") print("=" * 60) shipments = client.get_all_shipments_paginated( state=args.state, page_size=args.page_size, max_pages=args.max_pages, progress_callback=progress, ) print(f"\n✅ Fetched {len(shipments)} shipments total") if not shipments: print("\nNo shipments found.") return # Analyze the data print(f"\n{'='*60}") print("Analysis") print("=" * 60) # Collect statistics locales = {} countries = {} eans = set() customers = set() total_items = 0 for shipment in shipments: order = shipment.get("order", {}) # Locale locale = order.get("locale", "unknown") locales[locale] = locales.get(locale, 0) + 1 # Country ship_addr = order.get("shipAddress", {}) or {} country = ship_addr.get("country", {}) or {} country_iso = country.get("iso", "unknown") countries[country_iso] = countries.get(country_iso, 0) + 1 # Customer email = order.get("email") if email: customers.add(email) # Items units = shipment.get("inventoryUnits", []) total_items += len(units) for unit in units: variant = unit.get("variant", {}) or {} trade_id = variant.get("tradeId") or {} ean = trade_id.get("number") if ean: eans.add(ean) print(f"\nOrders: {len(shipments)}") print(f"Unique customers: {len(customers)}") print(f"Total items: {total_items}") print(f"Unique EANs: {len(eans)}") print(f"\nOrders by locale:") for locale, count in sorted(locales.items(), key=lambda x: -x[1]): print(f" {locale}: {count}") print(f"\nOrders by country:") for country, count in sorted(countries.items(), key=lambda x: -x[1]): print(f" {country}: {count}") # Show sample shipment print(f"\n{'='*60}") print("Sample Shipment") print("=" * 60) sample = shipments[0] order = sample.get("order", {}) ship_addr = order.get("shipAddress", {}) or {} units = sample.get("inventoryUnits", []) print(f"\nShipment #: {sample.get('number')}") print(f"Order #: {order.get('number')}") print(f"Customer: {order.get('email')}") print(f"Locale: {order.get('locale')}") print(f"Total: {order.get('total')} EUR") print(f"Ship to: {ship_addr.get('firstName')} {ship_addr.get('lastName')}") print(f"City: {ship_addr.get('zipCode')} {ship_addr.get('city')}") print(f"\nItems ({len(units)}):") for unit in units[:3]: variant = unit.get("variant", {}) or {} product = variant.get("product", {}) or {} trade_id = variant.get("tradeId") or {} name = product.get("name", {}) product_name = name.get("en") or name.get("fr") or name.get("de") or "?" print(f"\n - {product_name}") print(f" EAN: {trade_id.get('number')} ({trade_id.get('parser')})") print(f" SKU: {variant.get('sku')}") print(f" Price: {variant.get('price')} EUR") # Show EANs for matching test print(f"\n{'='*60}") print(f"Sample EANs ({min(10, len(eans))} of {len(eans)})") print("=" * 60) for ean in list(eans)[:10]: print(f" {ean}") print(f"\n{'='*60}") print("Test Complete!") print("=" * 60) print("\nTo import these orders, use the API endpoint:") print(" POST /api/v1/admin/letzshop/vendors/{vendor_id}/import-history") print("\nOr run via curl:") print(' curl -X POST "http://localhost:8000/api/v1/admin/letzshop/vendors/1/import-history?state=confirmed"') if __name__ == "__main__": main()