#!/usr/bin/env python3 # ============================================================================= # installer/serial_generator.py — AR Electronics serial number generator # ============================================================================= # # Developer tool. Generates a batch of unique serial numbers and optionally # writes them to a CSV log for the AR Electronics CRM. # # Format: AR-XXXX-XXXX-XXXX (hex groups, 48 bits of entropy ≈ 281 trillion) # # Usage: # python serial_generator.py 10 # generate 10 serials # python serial_generator.py 10 --csv serials.csv # python serial_generator.py 1 --vessel "MY YACHT NAME" --csv serials.csv # ============================================================================= import argparse import csv import os import secrets from datetime import datetime, timezone def generate_serial() -> str: """Generate a single AR-XXXX-XXXX-XXXX serial number.""" raw = secrets.token_hex(6).upper() # 6 bytes = 12 hex chars = 3 × 4 return f"AR-{raw[0:4]}-{raw[4:8]}-{raw[8:12]}" def generate_batch(count: int, vessel: str = "") -> list[dict]: serials = [] seen: set[str] = set() while len(serials) < count: serial = generate_serial() if serial in seen: continue # collision (astronomically unlikely) seen.add(serial) serials.append( { "serial": serial, "vessel": vessel, "created_at": datetime.now(timezone.utc).isoformat(), "status": "unactivated", } ) return serials def write_key_file(serial: str, output_path: str) -> None: """Write a single serial number to a serial.key file.""" with open(output_path, "w", encoding="utf-8") as f: f.write(serial) print(f" → {output_path}") def write_csv(records: list[dict], csv_path: str) -> None: """Append records to a CSV log (creates file if missing).""" file_exists = os.path.exists(csv_path) with open(csv_path, "a", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["serial", "vessel", "created_at", "status"]) if not file_exists: writer.writeheader() writer.writerows(records) print(f"\nAnexado a CSV: {csv_path}") def main(): parser = argparse.ArgumentParser( description="AR Electronics — Generador de números de serie" ) parser.add_argument("count", type=int, nargs="?", default=1, help="Cantidad de seriales a generar (default: 1)") parser.add_argument("--vessel", default="", help="Nombre del buque para asignar al lote") parser.add_argument("--csv", help="Ruta al archivo CSV de registro (se crea o se añade)") parser.add_argument("--key-dir", help="Directorio donde escribir archivos serial.key individuales") args = parser.parse_args() records = generate_batch(args.count, vessel=args.vessel) print(f"\nSeriales generados ({args.count}):\n") for rec in records: vessel_info = f" [{rec['vessel']}]" if rec["vessel"] else "" print(f" {rec['serial']}{vessel_info}") if args.csv: write_csv(records, args.csv) if args.key_dir: os.makedirs(args.key_dir, exist_ok=True) for i, rec in enumerate(records): filename = f"serial_{i+1:03d}.key" if len(records) > 1 else "serial.key" write_key_file(rec["serial"], os.path.join(args.key_dir, filename)) print() if __name__ == "__main__": main()