Initial commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user