Files
DriverVault/API Server/scripts/seed_from_excel.py
T
2026-07-06 08:50:52 +02:00

157 lines
4.7 KiB
Python

"""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()