"""Seed the Car Control database from the original "Car Service.xlsx". Imports each car sheet (service log + parts catalog) by POSTing through the API Server, so the full stack (client -> API Server -> PocketBase) is exercised. Usage: pip install openpyxl requests set API_BASE=http://localhost:8080/api (default) python scripts/seed_from_excel.py "C:/Users/jania/Desktop/Car Service.xlsx" Idempotency: a car is created only if no car with the same name exists yet. """ import os import sys import datetime as dt # Force UTF-8 stdout so console output works on Windows code pages (cp1250). sys.stdout.reconfigure(encoding="utf-8") import requests import openpyxl API_BASE = os.environ.get("API_BASE", "http://localhost:8080/api").rstrip("/") DEFAULT_XLSX = r"C:/Users/jania/Desktop/Car Service.xlsx" def yesno(v): return isinstance(v, str) and v.strip().lower() == "yes" def split_make_model(title): parts = title.split(" ", 1) if len(parts) == 2: return parts[0], parts[1] return title, "" def get_existing_cars(): r = requests.get(f"{API_BASE}/cars", timeout=15) r.raise_for_status() return {c["name"]: c for c in r.json()} def create_car(name, oil_spec): make, model = split_make_model(name) payload = { "name": name, "make": make, "model": model, "serviceIntervalDays": 365, "serviceIntervalKm": 15000, "oilSpec": oil_spec or "", } r = requests.post(f"{API_BASE}/cars", json=payload, timeout=15) r.raise_for_status() return r.json() def create_service(car_id, date, km, e, f, g): payload = { "car": car_id, "date": date.strftime("%Y-%m-%dT00:00:00Z"), "km": int(km) if km else 0, "changedOil": yesno(e), "changedEngineAirFilter": yesno(f), "changedCabinAirFilter": yesno(g), } r = requests.post(f"{API_BASE}/service-records", json=payload, timeout=15) r.raise_for_status() return r.json() def create_part(car_id, name, number): payload = {"car": car_id, "name": str(name), "partNumber": str(number) if number else ""} r = requests.post(f"{API_BASE}/parts", json=payload, timeout=15) r.raise_for_status() return r.json() def seed_service_sheet(ws, existing): """Sheets like 'Toyota Yaris' / 'Toyota Avensis': service log + parts (M/N).""" name = ws.title # Oil spec lives in N2 (with the oil-filter etc. catalog below in M/N). oil_spec = ws["N2"].value if isinstance(oil_spec, str): oil_spec = " / ".join(line.strip() for line in oil_spec.splitlines() if line.strip()) if name in existing: print(f"• {name} — car exists, skipping") return car = create_car(name, oil_spec) cid = car["id"] print(f"✓ {name} — car created ({cid})") services = parts = 0 for row in range(4, ws.max_row + 1): date = ws.cell(row=row, column=1).value # A if isinstance(date, dt.datetime): km = ws.cell(row=row, column=2).value # B create_service( cid, date, km, ws.cell(row=row, column=5).value, # E ws.cell(row=row, column=6).value, # F ws.cell(row=row, column=7).value, # G ) services += 1 pname = ws.cell(row=row, column=13).value # M pnum = ws.cell(row=row, column=14).value # N if pname and row >= 3: # M3 onward are real parts (M2/N2 = oil header) create_part(cid, pname, pnum) parts += 1 print(f" {services} service records, {parts} parts") def seed_reference_sheet(ws, existing): """Small sheets like 'Corolla Mama': just a parts reference (A/B columns).""" name = ws.title if name in existing: print(f"• {name} — car exists, skipping") return car = create_car(name, None) cid = car["id"] print(f"✓ {name} — car created ({cid})") parts = 0 for row in range(1, ws.max_row + 1): pname = ws.cell(row=row, column=1).value # A pnum = ws.cell(row=row, column=2).value # B if pname: create_part(cid, pname, pnum) parts += 1 print(f" {parts} parts") def main(): path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_XLSX print(f"Reading {path}") print(f"Seeding via {API_BASE}") wb = openpyxl.load_workbook(path, data_only=True) existing = get_existing_cars() for ws in wb.worksheets: # Heuristic: a service sheet has the "Service" header layout (>= col G). if ws.max_column >= 7 and ws["A3"].value == "Date": seed_service_sheet(ws, existing) else: seed_reference_sheet(ws, existing) print("\nSeed complete.") if __name__ == "__main__": main()