From 194f95f72626b1c6c7c1fbed4171fab1404dd44f Mon Sep 17 00:00:00 2001 From: Floke Date: Sat, 24 Jan 2026 14:06:56 +0000 Subject: [PATCH] docs: restore and enhance MIGRATION_PLAN.md with full history and lessons learned --- MIGRATION_PLAN.md | 199 ++++++++++++++++++++++------------------------ 1 file changed, 95 insertions(+), 104 deletions(-) diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md index 7829c17e..51cae1f4 100644 --- a/MIGRATION_PLAN.md +++ b/MIGRATION_PLAN.md @@ -1,4 +1,4 @@ -# Migrations-Plan: Legacy GSheets -> Company Explorer (Robotics Edition v0.7.0) +# Migrations-Plan: Legacy GSheets -> Company Explorer (Robotics Edition v0.7.4) **Kontext:** Neuanfang für die Branche **Robotik & Facility Management**. **Ziel:** Ablösung von Google Sheets/CLI durch eine Web-App ("Company Explorer") mit SQLite-Backend. @@ -95,151 +95,142 @@ Wir kapseln das neue Projekt vollständig ab ("Fork & Clean"). ## 7. Historie & Fixes (Jan 2026) * **[CRITICAL] v0.7.4: Service Restoration & Logic Fix (Jan 24, 2026)** - * **Summary:** Identified and resolved a critical issue where `ClassificationService` contained empty placeholder methods, leading to "Others" classification and missing metrics. + * **Summary:** Identified and resolved a critical issue where `ClassificationService` contained empty placeholder methods (`pass`), leading to "Others" classification and missing metrics. * **Fixes Implemented:** - * **Service Restoration:** Completely re-implemented `classify_company_potential`, `_run_llm_classification_prompt`, and `_run_llm_metric_extraction_prompt` to restore AI functionality. + * **Service Restoration:** Completely re-implemented `classify_company_potential`, `_run_llm_classification_prompt`, and `_run_llm_metric_extraction_prompt` to restore AI functionality using `call_gemini_flash`. * **Standardization Logic:** Connected the `standardization_logic` formula parser (e.g., "Values * 100m²") into the metric extraction cascade. It now correctly computes `standardized_metric_value` (e.g., 352 beds -> 35,200 m²). - * **Verification:** Confirmed end-to-end flow from "New Company" -> "Healthcare - Hospital" -> "352 Betten" -> "35200 m²" via the UI "Play" button. + * **Verification:** Confirmed end-to-end flow from "New Company" -> "Healthcare - Hospital" -> "352 Betten" -> "35.200 m²" via the UI "Play" button. - * **[STABILITY] v0.7.3: Hardening Metric Parser & Regression Testing (Jan 23, 2026) [RESOLVED]** + * **[STABILITY] v0.7.3: Hardening Metric Parser & Regression Testing (Jan 23, 2026)** * **Summary:** A series of critical fixes were applied to the `MetricParser` to handle complex real-world scenarios, and a regression test suite was created to prevent future issues. - * **Fixes Implemented:** - * **Greilmeier Bug:** Removed aggressive sentence splitting on hyphens that was truncating text and causing the parser to miss numbers at the end of a phrase. + * **Specific Bug Fixes:** + * **Wolfra Bug ("802020"):** Logic to detect and remove trailing years from concatenated numbers (e.g., "Mitarbeiter: 802020" -> "80"). + * **Erding Bug ("Year Prefix"):** Logic to ignore year-like prefixes appearing before the actual metric (e.g., "Seit 2022 ... 200.000 Besucher"). + * **Greilmeier Bug ("Truncation"):** Removed aggressive sentence splitting on hyphens that was truncating text and causing the parser to miss numbers at the end of a phrase. * **Expected Value Cleaning:** The parser now aggressively strips units (like "m²") from the LLM's `expected_value` to ensure it can find the correct numeric target even if the source text contains multiple numbers. - * **Wolfra Bug:** Logic to detect and remove trailing years from concatenated numbers (e.g., "802020" -> "80") was confirmed and added to tests. - * **Erding Bug:** Logic to ignore year-like prefixes (e.g., "2022 ... 200.000") was confirmed and added to tests. - * **Regression Test Suite (`/backend/tests/test_metric_parser.py`):** - * Created to lock in the fixes for the three critical bugs (Wolfra, Erding, Greilmeier) and ensure long-term stability of the number extraction logic. - * **Status:** The parser is now significantly more robust. All known major bugs are resolved and protected by automated tests. Core API endpoints (Import, Override, Create) were also restored during this process. + * **Regression Test Suite:** Created `/backend/tests/test_metric_parser.py` to lock in these fixes. + + * **[STABILITY] v0.7.2: Robust Metric Parsing (Jan 23, 2026)** + * **Legacy Logic Restored:** Re-implemented the robust, regex-based number parsing logic (formerly in legacy helpers) as `MetricParser`. + * **German Formats:** Correctly handles "1.000" (thousands) vs "1,5" (decimal) and mixed formats. + * **Citation Cleaning:** Filters out Wikipedia citations like `[3]` and years in parentheses (e.g. "80 (2020)" -> 80). + * **Hybrid Extraction:** The ClassificationService now asks the LLM for the *text segment* and parses the number deterministically, fixing "LLM Hallucinations" (e.g. "1.005" -> 1). * **[STABILITY] v0.7.1: AI Robustness & UI Fixes (Jan 21, 2026)** - * **SDK Stabilität:** Umstellung auf `gemini-2.0-flash` im Legacy-SDK zur Behebung von `404 Not Found` Fehlern bei `1.5-flash-latest`. - * **API-Key Management:** Implementierung eines robusten Ladevorgangs für den Google API Key (Fallback von Environment-Variable auf lokale Datei `/app/gemini_api_key.txt`). - * **Classification Prompt:** Schärfung des Prompts auf "Best-Fit"-Entscheidungen, um zu konservative "Others"-Einstufungen bei klaren Kandidaten (z.B. Thermen) zu vermeiden. - * **Frontend Rendering:** Fix eines UI-Crashs im Inspector. Metriken werden jetzt auch angezeigt, wenn nur der standardisierte Wert (Fläche) vorhanden ist. Null-Safety für `.toLocaleString()` hinzugefügt. - * **Scraping:** Wiederherstellung der Stabilität durch Entfernung fehlerhafter `trafilatura` Abhängigkeiten; Nutzung von `BeautifulSoup` als robustem Standard. + * **SDK Stabilität:** Umstellung auf `gemini-2.0-flash` im Legacy-SDK zur Behebung von `404 Not Found` Fehlern. + * **API-Key Management:** Robustes Laden des Keys aus `/app/gemini_api_key.txt`. + * **Classification Prompt:** Schärfung auf "Best-Fit"-Entscheidungen (kein vorzeitiges "Others"). + * **Scraping:** Wechsel auf `BeautifulSoup` nach Problemen mit `trafilatura`. * **[MAJOR] v0.7.0: Quantitative Potential Analysis (Jan 20, 2026)** -... -... -## 11. Lessons Learned (Retrospektive Jan 21, 2026) + * **Zweistufige Analyse:** + 1. **Strict Classification:** Ordnet Firmen einer Notion-Branche zu (oder "Others"). + 2. **Metric Cascade:** Sucht gezielt nach der branchenspezifischen Metrik ("Scraper Search Term"). + * **Fallback-Kaskade:** Website -> Wikipedia -> SerpAPI (Google Search). + * **Standardisierung:** Berechnet vergleichbare Werte (z.B. m²) aus Rohdaten mit der `Standardization Logic`. + * **Datenbank:** Erweiterung der `companies`-Tabelle um Metrik-Felder. -1. **KI statt Regex für Zahlen:** Anstatt komplexe Python-Funktionen für deutsche Zahlenformate ("1,7 Mio.") zu schreiben, ist es stabiler, das LLM anzuweisen, den Wert direkt als Integer (1700000) zu liefern. -2. **Abhängigkeiten isolieren:** Änderungen an zentralen `core_utils.py` führen schnell zu Import-Fehlern in anderen Modulen. Spezifische Logik (wie Metrik-Parsing) sollte lokal im Service bleiben. -3. **UI Null-Safety:** Quantitative Daten sind oft unvollständig (z.B. Fläche vorhanden, aber Besucherzahl nicht). Das Frontend muss robust gegen `null`-Werte in den Metrik-Feldern sein, um den Render-Prozess nicht zu unterbrechen. -4. **SDK-Versionen:** Die Google-API ist in stetigem Wandel. Der explizite Rückgriff auf stabile Modelle wie `gemini-2.0-flash` ist im Legacy-SDK sicherer als die Nutzung von `-latest` Tags. + * **[UPGRADE] v0.6.x: Notion Integration & UI Improvements** + * **Notion SSoT:** Umstellung der Branchenverwaltung (`Industries`) auf Notion. + * **Sync Automation:** `backend/scripts/sync_notion_industries.py`. + * **Contacts Management:** Globale Kontaktliste, Bulk-Import, Marketing-Status. + * **UI Overhaul:** Light/Dark Mode, Grid View, Responsive Design. - * **Zweistufige Analyse:** - 1. **Strict Classification:** Ordnet Firmen einer Notion-Branche zu (oder "Others"). - 2. **Metric Cascade:** Sucht gezielt nach der branchenspezifischen Metrik ("Scraper Search Term"). - * **Fallback-Kaskade:** Website -> Wikipedia -> SerpAPI (Google Search). - * **Standardisierung:** Berechnet vergleichbare Werte (z.B. m²) aus Rohdaten mit der `Standardization Logic`. - * **Datenbank:** Erweiterung der `companies`-Tabelle um Metrik-Felder, Deprecation der `signals`-Tabelle. - * **Backend Fixes (Finalized Jan 20, 2026):** - * Behebung diverser `NameError` und `ImportError` in `backend/lib/core_utils.py`, `backend/services/scraping.py` und `backend/services/classification.py`. - * Korrektur der `ClassificationService` Initialisierung und des Aufrufs in `backend/app.py` zur Behebung des `TypeError: missing 1 required positional argument: 'db'`. - * Wiederherstellung fehlender API-Endpunkte in `backend/app.py` (z.B. `/api/companies/bulk`) nach zu aggressivem Refactoring. -* **[UPGRADE] v0.6.1: Notion Sync Fixes** - * **Mapping:** Korrektur des Mappings für `Metric Type` und `Scraper Search Term` (Notion Select Fields). - * **Truncate-and-Reload:** Sync-Skript löscht alte Daten vor dem Import (für `industries`), behält aber `robotics_categories` bei (Upsert), um FK-Constraints zu schützen. - * **Frontend:** Korrektur der Einheiten-Anzeige ("Unit") im Settings-Dialog. - -* **[UPGRADE] v0.6.0: Notion Single Source of Truth** - * Synchronisation von Branchen und Kategorien direkt aus Notion. - -* **[UPGRADE] v0.5.1: Robustness** - * Logging, Wikipedia-Optimierung, UI-Fixes. - -## 8. Eingesetzte Prompts (Account-Analyse v0.7.0) +## 8. Eingesetzte Prompts (Account-Analyse v0.7.4) ### 8.1 Strict Industry Classification Ordnet das Unternehmen einer definierten Branche zu. ```python -prompt = r""" -Du bist ein präziser Branchen-Klassifizierer. -... ---- ZU VERWENDENDE BRANCHEN-DEFINITIONEN (STRIKT) --- -{industry_definitions_json} -... -Wähle EINE der folgenden Branchen... Wenn keine zutrifft, wähle "Others". +prompt = f""" +Act as a strict B2B Industry Classifier. +Company: {company_name} +Context: {website_text[:3000]} + +Available Industries: +{json.dumps(industry_definitions, indent=2)} + +Task: Select the ONE industry that best matches the company. +If the company is a Hospital/Klinik, select 'Healthcare - Hospital'. +If none match well, select 'Others'. + +Return ONLY the exact name of the industry. """ ``` ### 8.2 Metric Extraction -Extrahiert den spezifischen Zahlenwert ("Scraper Search Term"). +Extrahiert den spezifischen Zahlenwert ("Scraper Search Term") und liefert JSON für den `MetricParser`. ```python -prompt = r""" -Analysiere den folgenden Text... ---- KONTEXT --- -Branche: {industry_name} -Gesuchter Wert: '{search_term}' -... -Gib NUR ein JSON-Objekt zurück: -'raw_value', 'raw_unit', 'area_value' (falls explizit m² genannt). +prompt = f""" +Extract the following metric for the company in industry '{industry_name}': +Target Metric: "{search_term}" + +Source Text: +{text_content[:6000]} + +Return a JSON object with: +- "raw_value": The number found (e.g. 352 or 352.0). If text says "352 Betten", extract 352. If not found, null. +- "raw_unit": The unit found (e.g. "Betten", "m²"). +- "proof_text": A short quote from the text proving this value. + +JSON ONLY. """ ``` -## 9. Notion Integration +## 9. Notion Integration (Single Source of Truth) -Das System nutzt Notion als SSoT für `Industries` und `RoboticsCategories`. -Sync-Skript: `backend/scripts/sync_notion_industries.py`. +Das System nutzt Notion als zentrales Steuerungselement für strategische Definitionen. + +### 9.1 Datenfluss +1. **Definition:** Branchen und Robotik-Kategorien werden in Notion gepflegt (Whale Thresholds, Keywords, Definitionen). +2. **Synchronisation:** Das Skript `sync_notion_industries.py` zieht die Daten via API und führt einen Upsert in die lokale SQLite-Datenbank aus. +3. **App-Nutzung:** Das Web-Interface zeigt diese Daten schreibgeschützt an. Der `ClassificationService` nutzt sie als "System-Anweisung" für das LLM. + +### 9.2 Technische Details +* **Notion Token:** Muss in `/app/notion_token.txt` (Container-Pfad) hinterlegt sein. +* **DB-Mapping:** Die Zuordnung erfolgt primär über die `notion_id`, sekundär über den Namen, um Dubletten bei der Migration zu vermeiden. ## 10. Database Migration -Bei Schema-Änderungen ohne Datenverlust: `backend/scripts/migrate_db.py`. +Wenn die `industries`-Tabelle in einer bestehenden Datenbank aktualisiert werden muss (z.B. um neue Felder aus Notion zu unterstützen), darf die Datenbankdatei **nicht** gelöscht werden. Stattdessen muss das Migrations-Skript ausgeführt werden. -### 11.1 Lessons Learned (Retrospektive Jan 24, 2026) +**Prozess:** + +1. **Sicherstellen, dass die Zieldatenbank vorhanden ist:** Die `companies_v3_fixed_2.db` muss im `company-explorer`-Verzeichnis liegen (bzw. via Volume gemountet sein). +2. **Migration ausführen:** Dieser Befehl fügt die fehlenden Spalten hinzu, ohne Daten zu löschen. + ```bash + docker exec -it company-explorer python3 backend/scripts/migrate_db.py + ``` +3. **Container neu starten:** Damit der Server das neue Schema erkennt. + ```bash + docker-compose restart company-explorer + ``` +4. **Notion-Sync ausführen:** Um die neuen Spalten mit Daten zu befüllen. + ```bash + docker exec -it company-explorer python3 backend/scripts/sync_notion_industries.py + ``` + +## 11. Lessons Learned (Retrospektive Jan 24, 2026) 1. **API-Routing-Reihenfolge (FastAPI):** Ein spezifischer Endpunkt (z.B. `/api/companies/export`) muss **vor** einem dynamischen Endpunkt (z.B. `/api/companies/{company_id}`) deklariert werden. Andernfalls interpretiert FastAPI "export" als eine `company_id`, was zu einem `422 Unprocessable Entity` Fehler führt. 2. **Nginx `proxy_pass` Trailing Slash:** Das Vorhandensein oder Fehlen eines `/` am Ende der `proxy_pass`-URL in Nginx ist kritisch. Für Dienste wie FastAPI, die mit einem `root_path` (z.B. `/ce`) laufen, darf **kein** Trailing Slash verwendet werden (`proxy_pass http://company-explorer:8000;`), damit der `root_path` in der an das Backend weitergeleiteten Anfrage erhalten bleibt. -3. **Docker-Datenbank-Persistenz:** Das Fehlen eines expliziten Volume-Mappings für die Datenbankdatei in `docker-compose.yml` führt dazu, dass der Container eine interne, ephemere Kopie der Datenbank verwendet. Alle Änderungen, die außerhalb des Containers an der "Host"-DB vorgenommen werden, sind für die Anwendung unsichtbar. Es ist zwingend erforderlich, ein Mapping wie `./database.db:/app/database.db` zu definieren. -4. **Notion-Sync-Stabilität:** Der Sync-Prozess ist anfällig für Tippfehler in den Notion-Property-Namen (z.B. "Stanardization" statt "Standardization"). Dies führt zu stillen Fehlern, bei denen Felder einfach `None` sind. Bei fehlenden Daten muss dieses Skript zuerst überprüft werden. -5. **Formel-Robustheit (`Standardization Logic`):** Formeln, die aus externen Quellen (wie Notion) stammen, müssen aggressiv bereinigt werden. Kommentare in Klammern (z.B. `(Fläche pro Patient...)`) und Einheiten (`m²`) müssen vor der mathematischen Auswertung per `eval()` entfernt werden, um `NameError`- oder `SyntaxError`-Ausnahmen zu vermeiden. - - - -## 12. Deployment & Access Notes (Diskstation / Docker Compose) - +3. **Docker-Datenbank-Persistenz:** Das Fehlen eines expliziten Volume-Mappings für die Datenbankdatei in `docker-compose.yml` führt dazu, dass der Container eine interne, ephemere Kopie der Datenbank verwendet. Alle Änderungen, die außerhalb des Containers an der "Host"-DB vorgenommen werden, sind für die Anwendung unsichtbar. Es ist zwingend erforderlich, ein Mapping wie `./companies_v3_fixed_2.db:/app/companies_v3_fixed_2.db` zu definieren. +4. **Code-Integrität & Platzhalter:** Es ist kritisch, bei Datei-Operationen sicherzustellen, dass keine Platzhalter (wie `pass` oder `# omitted`) in den produktiven Code gelangen. Eine "Zombie"-Datei, die äußerlich korrekt aussieht aber innerlich leer ist, kann schwer zu debuggende Logikfehler verursachen. +5. **Formel-Robustheit:** Formeln aus externen Quellen müssen vor der Auswertung bereinigt werden (Entfernung von Einheiten, Kommentaren), um Syntax-Fehler zu vermeiden. +## 12. Deployment & Access Notes **Wichtiger Hinweis zum Deployment-Setup:** - - Dieses Projekt läuft in einer Docker-Compose-Umgebung, typischerweise auf einer Synology Diskstation. Der Zugriff auf die einzelnen Microservices erfolgt über einen zentralen Nginx-Reverse-Proxy (`proxy`-Service), der auf Port `8090` des Host-Systems lauscht. - - **Zugriffs-URLs für `company-explorer`:** - - -* **Intern (im Docker-Netzwerk):** Der `company-explorer`-Service lauscht intern auf Port `8000`. Direkter Zugriff ist nur von anderen Diensten im Docker-Compose-Netzwerk möglich. - -* **Extern (über Proxy):** Alle externen Zugriffe erfolgen über den Nginx-Proxy. - - * **Lokales Netzwerk (Beispiel):** `http://192.168.178.6:8090/ce/` - - * **Extern (über DuckDNS/HTTPS, Beispiel):** `https://floke-ai.duckdns.org/ce/` - - - -**Wichtige Routing-Hinweise:** - - - -* Der `company-explorer` FastAPI-Dienst ist so konfiguriert, dass er unter dem `root_path="/ce"` läuft. Alle API-Endpunkte (z.B. `/api/companies`, `/api/companies/export`) sind daher unter `/ce/api/...` zu erreichen, wenn sie über den Proxy aufgerufen werden. - -* Der Nginx-Proxy (`proxy`-Service) ist dafür zuständig, Anfragen an `/ce/` an den internen `company-explorer`-Dienst weiterzuleiten. Stellen Sie sicher, dass die `nginx-proxy.conf` korrekt konfiguriert ist, um alle relevanten Endpunkte (`/ce/api/companies/{id}`, `/ce/api/companies/export`) weiterzuleiten. - - +* **Intern (im Docker-Netzwerk):** `http://company-explorer:8000` +* **Extern (über Proxy):** `https://floke-ai.duckdns.org/ce/` (bzw. lokal `http://192.168.x.x:8090/ce/`) **Datenbank-Persistenz:** - - - -* Die SQLite-Datenbankdatei (`companies_v3_fixed_2.db`) muss mittels Docker-Volume-Mapping vom Host-Dateisystem in den `company-explorer`-Container gemountet werden (`./companies_v3_fixed_2.db:/app/companies_v3_fixed_2.db`). Dies stellt sicher, dass Datenänderungen persistent sind und nicht verloren gehen, wenn der Container neu gestartet oder neu erstellt wird. +* Die SQLite-Datenbankdatei (`companies_v3_fixed_2.db`) muss mittels Docker-Volume-Mapping vom Host-Dateisystem in den `company-explorer`-Container gemountet werden (`./companies_v3_fixed_2.db:/app/companies_v3_fixed_2.db`). Dies stellt sicher, dass Datenänderungen persistent sind und nicht verloren gehen, wenn der Container neu gestartet oder neu erstellt wird. \ No newline at end of file