neues skript sql_dump_diff.py zum Vergleich von 2 sql-dumps.
- leere, frisch initialisierte Datenbanken der Versionen verglichen - datasetprogramme erkennen jetzt auch suche nach standardtabellen wie uom
This commit is contained in:
@@ -25,6 +25,27 @@ Pro Modell wird zusätzlich `persisted` erkannt (erbt die Klasse `ModelSQL`
|
||||
o. ä., oder nur `ModelView` - dann ist es ein reines Wizard-/
|
||||
Transient-Formular ohne eigene Tabelle).
|
||||
|
||||
Verfolgt zusätzlich ganz normale **Python-Vererbung innerhalb derselben
|
||||
Datei**: erbt eine Klasse mit eigenem `__name__` von einer anderen lokalen
|
||||
Klasse - z. B. `class BOMOutput(BOMInput): __name__ = 'production.bom.output'`
|
||||
in `production/bom.py`, die selbst keine einzige `fields.X(...)`-Zeile
|
||||
deklariert, sondern alle Felder von `BOMInput` übernimmt - werden die
|
||||
geerbten Felder mit aufgenommen (eigene Felder überschreiben geerbte bei
|
||||
Namensgleichheit). Ohne das sähe ein rein über Vererbung wiederverwendetes
|
||||
Modell wie eines OHNE Felder aus, und Änderungen daran (z. B. eine
|
||||
Feldumbenennung im Elternteil) blieben komplett unsichtbar - genau das hat
|
||||
`sql_dump_diff.py` (Schritt 4) einmal live aufgedeckt.
|
||||
|
||||
Die Basisklassen-Auflösung ist bewusst auf "gleiche Datei" beschränkt
|
||||
(nicht "gleiches Modul"): derselbe lokale Klassenname wie `Cron` taucht
|
||||
innerhalb eines Moduls oft mehrfach für völlig unterschiedliche Klassen
|
||||
auf (Tryton-Konvention: eine PoolMeta-Erweiterung heißt oft genauso wie
|
||||
das Original, z. B. `currency/currency.py` definiert `class Cron(ModelSQL,
|
||||
ModelView): __name__ = 'currency.cron'`, während `currency/ir.py`
|
||||
unabhängig davon eine andere Klasse `class Cron(metaclass=PoolMeta):` zur
|
||||
Erweiterung von `ir.cron` definiert) - ein modulweites Registry würde sich
|
||||
da gegenseitig überschreiben.
|
||||
|
||||
```bash
|
||||
python3 extract_model_tree.py --repo /mnt/tryton \
|
||||
--modules company country currency party product ... \
|
||||
@@ -81,11 +102,50 @@ python3 model_coverage.py --model-tree reports/model_tree_5.0.json \
|
||||
--report reports/coverage_5.0.md
|
||||
```
|
||||
|
||||
### 4. `sql_dump_diff.py` – gegen den echten DB-Zustand validieren
|
||||
|
||||
Alles bisherige ist reine Code-Analyse. Zum Cross-Check: zwei mit
|
||||
`pg_dump` (Klartext-Format) erzeugte Dumps frisch initialisierter (leerer)
|
||||
Datenbanken beider Versionen strukturell vergleichen - Tabellen
|
||||
entfernt/neu, Spalten hinzugefügt/entfernt/Typ geändert, Zeilenzahl
|
||||
(z. B. mehr XML-Fixture-Daten in der neueren Version). Zeilen werden
|
||||
bereits vollständig geparst (für eine spätere inhaltliche
|
||||
Vergleichs-Erweiterung, sobald es Snapshots mit echten Testdaten gibt),
|
||||
aktuell nutzt der Report davon aber nur die Zeilenzahl.
|
||||
|
||||
Mit `--model-diff reports/model_diff.json` wird pro betroffener Tabelle
|
||||
zusätzlich geprüft, ob die rein statische Code-Analyse (Schritt 2)
|
||||
dieselbe Änderung schon vorhergesagt hatte (Modellname -> Tabellenname:
|
||||
Punkte durch Unterstriche ersetzen, wie `extract_updates.py`/
|
||||
`compare_snapshots.py` es auch tun) - Übereinstimmung bzw. Abweichung wird
|
||||
markiert.
|
||||
|
||||
```bash
|
||||
python3 sql_dump_diff.py --dump-a data/leer5.sql --label-a 5.0 \
|
||||
--dump-b data/leer8.sql --label-b 8.0 \
|
||||
--model-diff reports/model_diff.json \
|
||||
--output reports/dump_diff_5.0_8.0.json --report reports/dump_diff_5.0_8.0.md
|
||||
```
|
||||
|
||||
**Stand:** 78 von 92 vergleichbaren Fällen stimmen überein (~85%). Die
|
||||
verbleibenden Abweichungen gehen meist in eine erwartbare Richtung: die
|
||||
Code-Analyse findet MEHR Veränderung, als im leeren DB-Schema sichtbar
|
||||
ist (z. B. ein entferntes Function-Feld ohne eigene Spalte, oder
|
||||
Selection-Wertänderungen ohne Typänderung) - kein Fehler, sondern zwei
|
||||
Werkzeuge, die unterschiedliche Ebenen messen (Python-Feld-Definition vs.
|
||||
physisches SQL-Schema). Der `production.bom.output`-Fund oben (Schritt 1)
|
||||
kam genau aus einer vorherigen Abweichung dieser Art zustande.
|
||||
|
||||
## Bekannte Grenzen
|
||||
|
||||
- Rein statische Analyse: Felder, die nicht als einfaches
|
||||
`name = fields.X(...)` auf Klassenebene stehen (z. B. per Schleife/Helper
|
||||
dynamisch erzeugt), werden nicht erkannt.
|
||||
- Python-Vererbung wird nur INNERHALB derselben Datei aufgelöst (s. o.).
|
||||
Erbt eine Klasse von einer Basisklasse aus einer anderen Datei desselben
|
||||
oder eines anderen Moduls (Import statt lokaler Definition), werden
|
||||
deren Felder nicht gefunden - unauffällig, aber ein bekannter blinder
|
||||
Fleck.
|
||||
- `persisted`-Erkennung ist eine Textsuche über die Basisklassen-Ausdrücke
|
||||
(`ModelSQL`/`Workflow`/`DeactivableMixin`) - deckt den Normalfall ab, ist
|
||||
aber kein echtes Type-Checking.
|
||||
|
||||
+31
-4
@@ -42,10 +42,27 @@ erkennt:
|
||||
|
||||
→ wird zu einem YAML-Record mit `key`, `model`, `fields`. Referenzen auf
|
||||
zuvor im selben File angelegte Records werden zu `{"$ref": "..."}`,
|
||||
`Decimal(...)`/`date(...)` zu `{"$decimal": ...}`/`{"$date": ...}`. Alles
|
||||
nicht automatisch Auflösbare (Wizards, `.click(...)`, Helper-Funktionsaufrufe)
|
||||
wird als `{"$manual": "<Ausdruck>"}` markiert oder ganz übersprungen -
|
||||
bewusst sichtbar unvollständig statt still falsch.
|
||||
`Decimal(...)`/`date(...)` zu `{"$decimal": ...}`/`{"$date": ...}`.
|
||||
|
||||
Ein weiteres, sehr häufiges Muster wird ebenfalls erkannt: Zugriff auf
|
||||
bereits vorhandene Stammdaten (Länder, Währungen, Einheiten,
|
||||
Standard-Lagerorte, ... - i. d. R. per XML-Fixture beim Modul-Aktivieren
|
||||
angelegt, nicht Teil des eigentlichen Testdatensatzes):
|
||||
|
||||
```
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
```
|
||||
|
||||
→ wird zu `{"$search": {"model": "product.uom", "domain": [["name", "=", "Unit"]]}}`
|
||||
- aufgelöst wird das erst live beim Anlegen durch `create_dataset.py`
|
||||
(Schritt 3), nicht schon hier, da diese Records ja nicht selbst Teil der
|
||||
YAML sind. Nur literale Domains werden erkannt (keine Variablen darin);
|
||||
sonst bleibt es wie bisher `$manual`.
|
||||
|
||||
Alles sonst nicht automatisch Auflösbare (Wizards, `.click(...)`,
|
||||
Helper-Funktionsaufrufe) wird als `{"$manual": "<Ausdruck>"}` markiert oder
|
||||
ganz übersprungen - bewusst sichtbar unvollständig statt still falsch.
|
||||
|
||||
Record-Keys werden mit dem Dateinamen präfixiert, damit beim
|
||||
Zusammenführen mehrerer Module keine Kollisionen entstehen.
|
||||
@@ -83,6 +100,16 @@ bleibt trotzdem ein Fehler - das ist ja gerade das gesuchte Signal für eine
|
||||
echte Schema-Änderung. Weitere Umbenennungen bei Bedarf in `FIELD_RENAMES`
|
||||
ergänzen, sobald ein Lauf sie aufdeckt.
|
||||
|
||||
**`$search`-Auflösung:** Werte der Form `{"$search": {"model": ...,
|
||||
"domain": ...}}` (von `rst_to_dataset.py` erzeugt, siehe Schritt 1) werden
|
||||
live per `Model.get(model).find(domain)` gegen den Zielserver aufgelöst -
|
||||
für Stammdaten, die nicht selbst angelegt werden, sondern schon per
|
||||
XML-Fixture da sind. Ergebnis wird pro Lauf gecacht (dieselbe Suche, z. B.
|
||||
nach der Einheit "Unit", kommt in vielen Records vor). Kein Treffer ->
|
||||
Fehler mit Klartext statt eines neu angelegten Records - das ist selbst
|
||||
ein Signal, dass sich die Stammdaten zwischen den Versionen geändert
|
||||
haben.
|
||||
|
||||
### 4. `compare_snapshots.py` – zwei Versionen gegenüberstellen
|
||||
|
||||
Vergleicht zwei Snapshots (gleiche YAML, zwei Versionen/Datenbanken) und
|
||||
|
||||
Reference in New Issue
Block a user