diff --git a/README.md b/README.md new file mode 100644 index 0000000..6dedb6f --- /dev/null +++ b/README.md @@ -0,0 +1,250 @@ +# excel-import + +Generisches Kommandozeilen-Tool zum Import von Excel-Dateien (`.xls` und `.xlsx`) in Oracle- und PostgreSQL-Datenbanken. + +## Voraussetzungen + +- Python 3.10+ +- Für PostgreSQL: `psycopg2-binary` +- Für Oracle: `oracledb` (kein Oracle Client nötig, nutzt den Thin-Modus) + +## Installation + +```bash +pip install -e . +``` + +## Schnellstart + +```bash +# 1. Struktur der Excel-Datei anzeigen +excel-import inspect meine_datei.xlsx + +# 2. Starter-Konfiguration automatisch erzeugen +excel-import generate-config meine_datei.xlsx --dsn "postgresql+psycopg2://user:pass@localhost/mydb" + +# 3. Konfiguration anpassen (siehe unten) +# 4. Import ausführen +excel-import run meine_datei.xlsx import_config.yaml +``` + +--- + +## Befehle + +### `inspect` + +Zeigt die Sheets, Spalten und Zeilenzahl einer Excel-Datei — ohne Datenbank-Verbindung. + +```bash +excel-import inspect datei.xlsx +``` + +Beispielausgabe: + +``` +Sheets in datei.xlsx: + [0] Artikel + Columns (4): Artikelnummer, Bezeichnung, Preis, Interne Notiz + Rows: 1250 + [1] Kunden + Columns (3): Kundennummer, Name, E-Mail + Rows: 340 +``` + +--- + +### `generate-config` + +Erzeugt eine YAML-Konfigurationsdatei aus der Struktur der Excel-Datei. Die Datei kann danach manuell angepasst werden. + +```bash +excel-import generate-config datei.xlsx \ + --dsn "postgresql+psycopg2://user:pass@localhost/mydb" \ + --output config.yaml +``` + +| Option | Standard | Beschreibung | +|--------|----------|--------------| +| `--dsn` | PostgreSQL-Beispiel | SQLAlchemy-DSN der Zieldatenbank | +| `--output` / `-o` | `import_config.yaml` | Ausgabedatei | + +--- + +### `run` + +Führt den Import anhand einer YAML-Konfigurationsdatei durch. + +```bash +excel-import run datei.xlsx config.yaml +excel-import run datei.xlsx config.yaml --verbose +``` + +| Option | Beschreibung | +|--------|--------------| +| `-v` / `--verbose` | Ausführliche Ausgabe inkl. SQL-Details | + +--- + +## Konfigurationsdatei + +Die YAML-Konfiguration steuert, welche Sheets importiert werden, wie Spalten gemappt werden und welcher Import-Modus verwendet wird. + +```yaml +# SQLAlchemy DSN — Beispiele: +# PostgreSQL: postgresql+psycopg2://user:pass@localhost/mydb +# Oracle: oracle+oracledb://user:pass@localhost:1521/?service_name=MYDB +dsn: "postgresql+psycopg2://user:pass@localhost/mydb" + +default_varchar_length: 255 # Fallback-Länge für Text-Spalten + +sheets: + - sheet: "Artikel" # Sheet-Name oder Index (0, 1, ...) + header_row: 0 # 0-basierter Zeilenindex der Kopfzeile + skip_rows: 0 # Zeilen vor der Kopfzeile überspringen + target_table: "artikel" # Ziel-Tabelle (wird angelegt, falls nicht vorhanden) + mode: "replace" # append | replace | upsert + upsert_keys: [] # Primärschlüssel-Spalten für Upsert + columns: + - source: "Artikelnummer" + target: "artikelnummer" + dtype: "VARCHAR(50)" # optional: Typ-Override + - source: "Bezeichnung" + target: "bezeichnung" + - source: "Preis" + target: "preis" + dtype: "NUMERIC(12,2)" + - source: "Interne Notiz" + target: "interne_notiz" + skip: true # Spalte nicht importieren +``` + +### Import-Modi + +| Modus | Verhalten | +|-------|-----------| +| `append` | Zeilen werden an die bestehende Tabelle angehängt | +| `replace` | Tabelle wird geleert (TRUNCATE), dann neu befüllt | +| `upsert` | Zeilen werden eingefügt oder aktualisiert (anhand `upsert_keys`) | + +### Spalten-Konfiguration + +| Feld | Pflicht | Beschreibung | +|------|---------|--------------| +| `source` | ja | Spaltenname in der Excel-Datei | +| `target` | ja | Spaltenname in der Datenbank | +| `dtype` | nein | Typ-Override, z.B. `VARCHAR(100)`, `NUMERIC(12,2)`, `DATE` | +| `skip` | nein | Spalte komplett ignorieren (`true`/`false`) | + +Unterstützte Typ-Overrides: `VARCHAR(n)`, `TEXT`, `CLOB`, `INTEGER`, `NUMBER`, `NUMERIC(p,s)`, `DECIMAL(p,s)`, `FLOAT`, `DATE`, `DATETIME`, `TIMESTAMP`, `BOOLEAN`. + +Ohne `dtype` erkennt das Tool den Typ automatisch aus den Daten. + +### Verbindungs-DSN + +**PostgreSQL:** +``` +postgresql+psycopg2://user:password@host:5432/datenbankname +``` + +**Oracle (Thin-Modus, kein Client nötig):** +``` +oracle+oracledb://user:password@host:1521/?service_name=MYSERVICE +``` + +``` +oracle+oracledb://user:password@host:1521/SID +``` + +--- + +## Tabellen-Verwaltung + +Existiert die Ziel-Tabelle noch nicht, wird sie automatisch angelegt. Das Schema wird aus den Daten und der Spalten-Konfiguration abgeleitet. + +Existiert die Tabelle bereits, werden die Daten entsprechend dem gewählten Modus eingefügt — die Tabellenstruktur wird **nicht** geändert. Soll das Schema angepasst werden, muss die Tabelle vorher manuell geändert oder gelöscht werden. + +--- + +## Beispiele + +### Vollständiger Import mit Upsert + +```yaml +dsn: "postgresql+psycopg2://user:pass@localhost/mydb" +sheets: + - sheet: "Kunden" + target_table: "kunden" + mode: "upsert" + upsert_keys: ["kundennummer"] + columns: + - source: "Kundennummer" + target: "kundennummer" + dtype: "VARCHAR(20)" + - source: "Name" + target: "name" + - source: "E-Mail" + target: "email" +``` + +### Sheet per Index ansprechen + +```yaml +sheets: + - sheet: 2 # drittes Sheet (0-basiert) + header_row: 1 # Kopfzeile ist in Zeile 2 (0-basiert: 1) + skip_rows: 0 + target_table: "rohdaten" + mode: "append" + columns: [] # alle Spalten importieren, keine Umbenennung +``` + +### Mehrere Sheets in einem Lauf + +```yaml +dsn: "oracle+oracledb://scott:tiger@localhost:1521/?service_name=ORCL" +sheets: + - sheet: "Artikel" + target_table: "artikel" + mode: "replace" + columns: + - source: "ArtNr" + target: "art_nr" + dtype: "VARCHAR(50)" + - source: "Preis" + target: "preis" + dtype: "NUMERIC(12,2)" + + - sheet: "Lager" + target_table: "lagerbestand" + mode: "replace" + columns: [] +``` + +--- + +## Projektstruktur + +``` +excel-import/ +├── pyproject.toml +├── examples/ +│ └── import_config.yaml +├── excel_import/ +│ ├── config.py # Konfigurationsklassen + YAML-Loader +│ ├── reader.py # Excel-Einlesen (.xls + .xlsx) +│ ├── schema.py # Automatische Typ-Erkennung + DDL +│ ├── importer.py # Import-Logik (append/replace/upsert) +│ └── cli.py # Kommandozeilen-Interface +└── tests/ + ├── test_config.py + ├── test_reader.py + └── test_importer.py +``` + +## Tests ausführen + +```bash +pip install -e ".[dev]" +pytest +```