Files
Brancheneinstufung2/MIGRATION_PLAN.md
Floke 0766637ae1 feat(Explorer): Enhance metric extraction, source transparency, and UI display
- **Standardization & Formula Logic:** Fixed NameError/SyntaxError in formula parser; added support for comments and capitalized placeholders.
- **Source URL Tracking:** Extended DB schema and cascade logic to store and track specific source URLs.
- **Frontend & UI:**
  - Added 'Standardized Potential' display in Inspector.
  - Added clickable source link with icon.
  - Fixed Settings tab layout collapse (flex-shrink-0).
- **Export Capabilities:**
  - Single-company JSON export now includes full quantitative metadata.
  - New global CSV export endpoint /api/companies/export.
- **System Integrity:**
  - Fixed Notion sync typo ('Stanardization').
  - Corrected Nginx proxy routing and FastAPI route ordering.
  - Ensured DB persistence via explicit docker-compose volume mapping.
2026-01-24 09:56:59 +00:00

14 KiB

Migrations-Plan: Legacy GSheets -> Company Explorer (Robotics Edition v0.7.0)

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.

1. Strategische Neuausrichtung

Bereich Alt (Legacy) Neu (Robotics Edition)
Daten-Basis Google Sheets SQLite (Lokal, performant, filterbar).
Ziel-Daten Allgemein / Kundenservice Quantifizierbares Potenzial (z.B. 4500m² Fläche, 120 Betten).
Branchen KI-Vorschlag (Freitext) Strict Mode: Mapping auf definierte Notion-Liste (z.B. "Hotellerie", "Automotive").
Bewertung 0-100 Score (Vage) Data-Driven: Rohwert (Scraper/Search) -> Standardisierung (Formel) -> Potenzial.
Analytics Techniker-ML-Modell Deaktiviert. Fokus auf harte Fakten.
Operations D365 Sync (Broken) Excel-Import & Deduplizierung. Fokus auf Matching externer Listen gegen Bestand.

2. Architektur & Komponenten-Mapping

Das System wird in company-explorer/ neu aufgebaut. Wir lösen Abhängigkeiten zur Root helpers.py auf.

A. Core Backend (backend/)

Komponente Aufgabe & Neue Logik Prio
Database Ersetzt GoogleSheetHandler. Speichert Firmen & "Enrichment Blobs". 1
Importer Ersetzt SyncManager. Importiert Excel-Dumps (CRM) und Event-Listen. 1
Deduplicator Ersetzt company_deduplicator.py. Kern-Feature: Checkt Event-Listen gegen DB. Muss "intelligent" matchen (Name + Ort + Web). 1
Scraper (Base) Extrahiert Text von Websites. Basis für alle Analysen. 1
Classification Service NEU (v0.7.0). Zweistufige Logik:
1. Strict Industry Classification.
2. Metric Extraction Cascade (Web -> Wiki -> SerpAPI).
1
Marketing Engine Ersetzt generate_marketing_text.py. Nutzt neue marketing_wissen_robotics.yaml. 3

B. Frontend (frontend/) - React

  • View 1: Der "Explorer": DataGrid aller Firmen. Filterbar nach "Roboter-Potential" und Status.
  • View 2: Der "Inspector": Detailansicht einer Firma. Zeigt gefundene Signale ("Hat SPA Bereich"). Manuelle Korrektur-Möglichkeit.
  • View 3: "List Matcher": Upload einer Excel-Liste -> Anzeige von Duplikaten -> Button "Neue importieren".
  • View 4: "Settings": Konfiguration von Branchen, Rollen und Robotik-Logik.

3. Umgang mit Shared Code (helpers.py & Co.)

Wir kapseln das neue Projekt vollständig ab ("Fork & Clean").

  • Quelle: helpers.py (Root)
  • Ziel: company-explorer/backend/lib/core_utils.py
  • Aktion: Wir kopieren nur relevante Teile und ergänzen sie (z.B. safe_eval_math, run_serp_search).

4. Datenstruktur (SQLite Schema)

Tabelle companies (Stammdaten & Analyse)

  • id (PK)
  • name (String)
  • website (String)
  • crm_id (String, nullable - Link zum D365)
  • industry_crm (String - Die "erlaubte" Branche aus Notion)
  • city (String)
  • country (String - Standard: "DE" oder aus Impressum)
  • status (Enum: NEW, IMPORTED, ENRICHED, QUALIFIED)
  • NEU (v0.7.0):
    • calculated_metric_name (String - z.B. "Anzahl Betten")
    • calculated_metric_value (Float - z.B. 180)
    • calculated_metric_unit (String - z.B. "Betten")
    • standardized_metric_value (Float - z.B. 4500)
    • standardized_metric_unit (String - z.B. "m²")
    • metric_source (String - "website", "wikipedia", "serpapi")

Tabelle signals (Deprecated)

  • Veraltet ab v0.7.0. Wird durch quantitative Metriken in companies ersetzt.

Tabelle contacts (Ansprechpartner)

  • id (PK)
  • account_id (FK -> companies.id)
  • gender, title, first_name, last_name, email
  • job_title (Visitenkarte)
  • role (Standardisierte Rolle: "Operativer Entscheider", etc.)
  • status (Marketing Status)

Tabelle industries (Branchen-Fokus - Synced from Notion)

  • id (PK)
  • notion_id (String, Unique)
  • name (String - "Vertical" in Notion)
  • description (Text - "Definition" in Notion)
  • metric_type (String - "Metric Type")
  • min_requirement (Float - "Min. Requirement")
  • whale_threshold (Float - "Whale Threshold")
  • proxy_factor (Float - "Proxy Factor")
  • scraper_search_term (String - "Scraper Search Term")
  • scraper_keywords (Text - "Scraper Keywords")
  • standardization_logic (String - "Standardization Logic")

Tabelle job_role_mappings (Rollen-Logik)

  • id (PK)
  • pattern (String - Regex für Jobtitles)
  • role (String - Zielrolle)

7. Historie & Fixes (Jan 2026)

*   **[STABILITY] v0.7.3: Hardening Metric Parser & Regression Testing (Jan 23, 2026) [RESOLVED]**
    *   **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.
        *   **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.

*   **[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.

*   **[MAJOR] v0.7.0: Quantitative Potential Analysis (Jan 20, 2026)**

... ...

11. Lessons Learned (Retrospektive Jan 21, 2026)

  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.

    • 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.1 Strict Industry Classification

Ordnet das Unternehmen einer definierten Branche zu.

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".
"""

8.2 Metric Extraction

Extrahiert den spezifischen Zahlenwert ("Scraper Search Term").

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).
"""

9. Notion Integration

Das System nutzt Notion als SSoT für Industries und RoboticsCategories. Sync-Skript: backend/scripts/sync_notion_industries.py.

10. Database Migration

Bei Schema-Änderungen ohne Datenverlust: backend/scripts/migrate_db.py.

11.1 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üssen vor der mathematischen Auswertung per eval() entfernt werden, um NameError- oder SyntaxError-Ausnahmen zu vermeiden.

12. Deployment & Access Notes (Diskstation / Docker Compose)

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.

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.