diff --git a/tools/import_environnement.py b/tools/import_environnement.py new file mode 100644 index 0000000..de3fd49 --- /dev/null +++ b/tools/import_environnement.py @@ -0,0 +1,123 @@ +"""Ajoute colonne environnement sur servers et importe depuis CSV iTop. + +Stocke les 7 valeurs iTop verbatim: Développement, Intégration, Pré-Prod, +Production, Recette, Test, Formation. + +Usage: + python tools/import_environnement.py [ ...] [--dry-run] +""" +import os +import csv +import argparse +from sqlalchemy import create_engine, text + +DATABASE_URL = os.getenv("DATABASE_URL_DEMO") or os.getenv("DATABASE_URL") \ + or "postgresql://patchcenter:PatchCenter2026!@localhost:5432/patchcenter_demo" + +ITOP_ENVS = [ + "Développement", "Intégration", "Pré-Prod", + "Production", "Recette", "Test", "Formation", +] + +# Aliases courants -> valeur iTop canonique +ENV_ALIASES = { + "developpement": "Développement", + "dev": "Développement", + "integration": "Intégration", + "int": "Intégration", + "preprod": "Pré-Prod", + "pre-prod": "Pré-Prod", + "pre prod": "Pré-Prod", + "prod": "Production", + "production": "Production", + "recette": "Recette", + "rec": "Recette", + "test": "Test", + "tests": "Test", + "formation": "Formation", +} + + +def norm_env(raw): + if not raw: + return None + s = raw.strip() + if not s or s in ("-", "(null)", "workgroup"): + return None + if s in ITOP_ENVS: + return s + return ENV_ALIASES.get(s.lower()) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("csv_paths", nargs="+") + parser.add_argument("--dry-run", action="store_true") + args = parser.parse_args() + + engine = create_engine(DATABASE_URL) + print(f"[INFO] DB: {DATABASE_URL.split('@')[-1]}") + conn = engine.connect().execution_options(isolation_level="AUTOCOMMIT") + + # 1. Ajoute colonne + CHECK + print("[INFO] Ajoute colonne environnement...") + if not args.dry_run: + conn.execute(text("ALTER TABLE servers ADD COLUMN IF NOT EXISTS environnement varchar(20)")) + conn.execute(text("ALTER TABLE servers DROP CONSTRAINT IF EXISTS servers_environnement_check")) + allowed_sql = ", ".join(f"'{v}'" for v in ITOP_ENVS) + conn.execute(text( + f"ALTER TABLE servers ADD CONSTRAINT servers_environnement_check " + f"CHECK (environnement IN ({allowed_sql}) OR environnement IS NULL)" + )) + print(f"[INFO] Envs iTop autorises: {ITOP_ENVS}") + + # 2. Relecture CSV -> update + updated = 0 + unchanged = 0 + not_found = 0 + unknown = set() + + for csv_path in args.csv_paths: + print(f"\n[INFO] Lecture {csv_path}") + 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 "," + reader = csv.DictReader(f, delimiter=delim) + rows = list(reader) + print(f"[INFO] {len(rows)} lignes (delim={delim!r})") + + for r in rows: + hostname = (r.get("Nom") or r.get("Hostname") or "").strip() + if not hostname or not any(c.isalpha() for c in hostname): + continue + raw = (r.get("Environnement") or r.get("Environment") or "").strip() + new_env = norm_env(raw) + if raw and new_env is None and raw not in ("-", "(null)", "workgroup", ""): + unknown.add(raw) + continue + if new_env is None: + continue + srv = conn.execute(text("SELECT id, environnement FROM servers WHERE hostname=:h"), + {"h": hostname}).fetchone() + if not srv: + not_found += 1 + continue + if srv.environnement == new_env: + unchanged += 1 + continue + if args.dry_run: + print(f" DRY: {hostname} {srv.environnement} -> {new_env}") + else: + conn.execute(text("UPDATE servers SET environnement=:e WHERE id=:sid"), + {"e": new_env, "sid": srv.id}) + updated += 1 + + conn.close() + print(f"\n[DONE] Maj: {updated} | Inchanges: {unchanged} | Hors base: {not_found}") + if unknown: + print(f"[WARN] Valeurs non mappees: {unknown}") + + +if __name__ == "__main__": + main()