157 lines
4.7 KiB
Python
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()
|