#!/usr/bin/env python3 """ Import clients depuis data.txt (format psql pipe-séparé) vers PostgreSQL. Usage : python3 import_clients.py [--dry-run] """ import os import sys import psycopg2 # ── Connexion ────────────────────────────────────────────────────────────────── DB_CONFIG = { "host": "localhost", "port": 5432, "user": "", "password": "", "dbname": "", } DATA_FILE = os.path.join(os.path.dirname(__file__), "data.txt") DRY_RUN = "--dry-run" in sys.argv # ── Ordre des colonnes dans data.txt (DB production, construite incrémentalement) # None = colonne inconnue/obsolète à ignorer SOURCE_COLUMNS = [ "id", "username", "password", "nom", "prenom", "telephone", "command", "amende", "cancel_commande", "referral_balance", # position 9 : NUMERIC(10,2) → "0.00" "cancellations_count", # position 10 : INTEGER → "0" None, # position 11 : colonne obsolète/inconnue, ignorée "last_penalty_reason", # position 12 : TEXT "updated_at", "created_at", "must_change_password", # position 15 : BOOLEAN → "f"/"t" ] def parse_value(v: str): """Convertit une valeur brute psql en type Python (None si vide).""" v = v.strip() if v == "" or v.lower() == "null": return None if v.lower() == "t": return True if v.lower() == "f": return False return v def load_data(path: str) -> list[list]: rows = [] with open(path, encoding="utf-8") as f: for line in f: line = line.rstrip("\n") if not line.strip(): continue # Lignes séparateur psql (ex: "----+----") if ( set( line.replace("|", "") .replace("+", "") .replace("-", "") .replace(" ", "") ) == set() ): continue parts = line.split("|") values = ( [parse_value(p) for p in parts[:-1]] if parts[-1].strip() == "" else [parse_value(p) for p in parts] ) if values: rows.append(values) return rows def main(): rows = load_data(DATA_FILE) if not rows: print("❌ Aucune ligne trouvée dans data.txt") sys.exit(1) print(f"📂 {len(rows)} lignes lues depuis {DATA_FILE}") data_col_count = len(rows[0]) print( f"📋 Colonnes data.txt : {data_col_count} | SOURCE_COLUMNS définis : {len(SOURCE_COLUMNS)}" ) if data_col_count != len(SOURCE_COLUMNS): print( f"⚠️ Ajuste SOURCE_COLUMNS dans le script ({len(SOURCE_COLUMNS)} définis, {data_col_count} dans le fichier)" ) sys.exit(1) # Colonnes à insérer (on filtre les None = colonnes ignorées) insert_cols = [col for col in SOURCE_COLUMNS if col is not None and col != "id"] # On conserve aussi l'id pour FORCE_ID insert_cols_with_id = ["id"] + insert_cols placeholders = ", ".join(["%s"] * len(insert_cols_with_id)) col_names = ", ".join(insert_cols_with_id) update_expr = ", ".join(f"{c} = EXCLUDED.{c}" for c in insert_cols) sql = f""" INSERT INTO clients ({col_names}) VALUES ({placeholders}) ON CONFLICT (username) DO UPDATE SET {update_expr} """ conn = psycopg2.connect(**DB_CONFIG) conn.autocommit = False cur = conn.cursor() inserted = 0 updated = 0 errors = 0 for i, row in enumerate(rows, 1): # Construire un dict {col_name: value} depuis SOURCE_COLUMNS row_dict = {} for pos, col in enumerate(SOURCE_COLUMNS): if col is None: continue row_dict[col] = row[pos] if pos < len(row) else None values = [row_dict.get(c) for c in insert_cols_with_id] username = row_dict.get("username", "?") try: if DRY_RUN: print(f" [DRY-RUN] ligne {i} : {username} → {row_dict}") inserted += 1 continue cur.execute("SELECT 1 FROM clients WHERE username = %s", (username,)) exists = cur.fetchone() is not None cur.execute(sql, values) if exists: updated += 1 else: inserted += 1 except Exception as e: print(f" ❌ Erreur ligne {i} ({username}) : {e}") conn.rollback() errors += 1 continue if not DRY_RUN: cur.execute( "SELECT setval('clients_id_seq', COALESCE((SELECT MAX(id) FROM clients), 1))" ) conn.commit() cur.close() conn.close() print(f"\n{'🔍 DRY-RUN — ' if DRY_RUN else ''}✅ Terminé") print(f" Insérés : {inserted}") print(f" Mis à jour : {updated}") print(f" Erreurs : {errors}") print(f" ℹ️ Séquence clients_id_seq resynchronisée sur MAX(id)") if __name__ == "__main__": main()