Fix: iTop exporte 2 colonnes Etat (lifecycle+condition), prendre la 1ere

Python csv.DictReader ne garde que le dernier quand 2 colonnes ont le
meme nom: le Nouveau (condition) ecrasait le Production (lifecycle).
Switch vers csv.reader + lecture par indice de colonne (1ere occurrence
de Etat = lifecycle).
This commit is contained in:
Pierre & Lumière 2026-04-14 19:05:08 +02:00
parent 3c6e10944e
commit a366427daf
2 changed files with 31 additions and 13 deletions

View File

@ -110,15 +110,29 @@ def main():
with open(csv_path, "r", encoding="utf-8-sig", newline="") as f:
sample = f.read(4096); f.seek(0)
delim = ";" if sample.count(";") > sample.count(",") else ","
rows = list(csv.DictReader(f, delimiter=delim))
print(f"[INFO] {len(rows)} lignes (delim={delim!r})")
for r in rows:
hostname = (r.get("Nom") or r.get("Hostname") or "").strip()
reader = csv.reader(f, delimiter=delim)
header = next(reader)
# iTop exporte parfois 2 colonnes "Etat" (lifecycle + condition physique).
# On prend la PREMIERE occurrence = lifecycle.
idx_etat = next((i for i, h in enumerate(header) if h in ("Etat", "État")), -1)
idx_nom = next((i for i, h in enumerate(header) if h == "Nom"), -1)
idx_hostname = next((i for i, h in enumerate(header) if h == "Hostname"), -1)
rows = list(reader)
print(f"[INFO] {len(rows)} lignes (delim={delim!r}, idx_etat={idx_etat})")
if idx_etat == -1:
print("[WARN] Colonne Etat absente, skip.")
continue
for row in rows:
if idx_nom >= 0 and idx_nom < len(row):
hostname = row[idx_nom].strip()
elif idx_hostname >= 0 and idx_hostname < len(row):
hostname = row[idx_hostname].strip()
else:
hostname = ""
if not hostname or not any(c.isalpha() for c in hostname):
continue
# Etat d'abord (lifecycle VM). Si valeur "condition" -> NULL.
# Status ignore (toujours 'Production' cote iTop, pas pertinent).
raw = (r.get("Etat") or r.get("État") or "").strip()
# Lifecycle = 1ere colonne Etat (l'iTop export peut avoir 2 "Etat")
raw = row[idx_etat].strip() if idx_etat < len(row) else ""
new_etat = norm_etat(raw)
# Cas: valeur condition iTop (Nouveau, Recycle, ...) -> NULL (physique non deploye)
is_condition = raw in ("Nouveau", "Recyclé", "A récupérer", "Cassé",

View File

@ -18,15 +18,19 @@ def main():
with open(path, "r", encoding="utf-8-sig", newline="") as f:
sample = f.read(4096); f.seek(0)
delim = ";" if sample.count(";") > sample.count(",") else ","
reader = csv.DictReader(f, delimiter=delim)
reader = csv.reader(f, delimiter=delim)
header = next(reader)
rows = list(reader)
print(f"{len(rows)} lignes (delim={delim!r})")
print(f"Colonnes: {list(rows[0].keys()) if rows else []}")
for col in ("Etat", "État", "Status", "Environnement"):
if rows and col in rows[0]:
c = Counter((r.get(col) or "").strip() for r in rows)
print(f"\n[{col}]")
# iTop peut avoir 2 colonnes "Etat" (lifecycle + condition) - les compter separement
cols_to_check = ["Etat", "État", "Status", "Environnement"]
for col in cols_to_check:
indices = [i for i, h in enumerate(header) if h == col]
for rank, idx in enumerate(indices, 1):
suffix = f" (#{rank})" if len(indices) > 1 else ""
c = Counter((row[idx].strip() if idx < len(row) else "") for row in rows)
print(f"\n[{col}{suffix} col index={idx}]")
for v, n in c.most_common():
print(f" {v!r:30s} {n}")