diff --git a/project/backend/coordinator/controller/kpi_setting_controller.py b/project/backend/coordinator/controller/kpi_setting_controller.py index 92b51b3..ff037a0 100644 --- a/project/backend/coordinator/controller/kpi_setting_controller.py +++ b/project/backend/coordinator/controller/kpi_setting_controller.py @@ -34,6 +34,7 @@ def create_kpi_setting(): "position", "active", "examples", + "is_trained", ] for field in required_fields: if field not in data: @@ -58,6 +59,7 @@ def create_kpi_setting(): position=data["position"], active=data["active"], examples=data.get("examples", []), + is_trained=False ) db.session.add(new_kpi_setting) diff --git a/project/backend/coordinator/controller/spacy_controller.py b/project/backend/coordinator/controller/spacy_controller.py index 97b64aa..eeb207c 100644 --- a/project/backend/coordinator/controller/spacy_controller.py +++ b/project/backend/coordinator/controller/spacy_controller.py @@ -6,6 +6,8 @@ from werkzeug.utils import secure_filename from model.database import db import os import requests +from model.kpi_setting_model import KPISettingModel + spacy_controller = Blueprint("spacy", __name__, url_prefix="/api/spacy") @@ -122,8 +124,14 @@ current_training_status = {"running": False} def update_training_status(): data = request.get_json() current_training_status["running"] = data.get("running", False) - running = current_training_status["running"] - print(f"[INFO] Trainingsstatus aktualisiert: running = {running}") + + if current_training_status["running"] is False: + try: + KPISettingModel.query.update({KPISettingModel.is_trained: True}) + db.session.commit() + except Exception as e: + db.session.rollback() + return jsonify({"error": "is_trained konnte nicht aktualisiert werden", "details": str(e)}), 500 return jsonify({"status": "success", "running": current_training_status["running"]}) diff --git a/project/backend/coordinator/model/kpi_setting_model.py b/project/backend/coordinator/model/kpi_setting_model.py index 4accfbb..37936b2 100644 --- a/project/backend/coordinator/model/kpi_setting_model.py +++ b/project/backend/coordinator/model/kpi_setting_model.py @@ -27,6 +27,8 @@ class KPISettingModel(db.Model): position: Mapped[int] active: Mapped[bool] examples: Mapped[list] = mapped_column(JSONB, default=[]) + is_trained: Mapped[bool] = mapped_column(default=False) + def to_dict(self): return OrderedDict( @@ -38,13 +40,15 @@ class KPISettingModel(db.Model): ("position", self.position), ("examples", self.examples), ("active", self.active), + ("is_trained", self.is_trained), ] ) - def __init__(self, name, mandatory, type, position, active, examples=None): + def __init__(self, name, mandatory, type, position, active, examples=None, is_trained=False): self.name = name self.mandatory = mandatory self.type = type self.position = position self.active = active self.examples = examples or [] + self.is_trained = is_trained diff --git a/project/backend/coordinator/model/seed_data.py b/project/backend/coordinator/model/seed_data.py index 6e34664..7ca5396 100644 --- a/project/backend/coordinator/model/seed_data.py +++ b/project/backend/coordinator/model/seed_data.py @@ -14,6 +14,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.STRING, "position": 1, "active": True, + "is_trained": True, "examples": [ { "sentence": "Der Fonds trägt den Namen Alpha Real Estate Fund I.", @@ -31,6 +32,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.STRING, "position": 2, "active": True, + "is_trained": True, "examples": [ { "sentence": "Fondsmanager des Projekts ist Max Mustermann.", @@ -48,6 +50,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.STRING, "position": 3, "active": True, + "is_trained": True, "examples": [ { "sentence": "AIFM ist die Alpha Investment Management GmbH.", @@ -65,6 +68,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.DATE, "position": 4, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die Daten basieren auf dem Stand vom 05.05.2025.", @@ -82,6 +86,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.STRING, "position": 5, "active": True, + "is_trained": True, "examples": [ { "sentence": "Der Fonds hat das Risikoprofil Core/Core++.", @@ -99,6 +104,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.BOOLEAN, "position": 6, "active": True, + "is_trained": True, "examples": [ { "sentence": "Der Fonds erfüllt die Anforderungen von Artikel 8.", @@ -116,6 +122,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.NUMBER, "position": 7, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die angestrebte Zielrendite liegt bei 6.5 %.", @@ -130,6 +137,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.NUMBER, "position": 8, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die Rendite für das Jahr beträgt 5.8 %.", @@ -147,6 +155,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.NUMBER, "position": 9, "active": True, + "is_trained": True, "examples": [ {"sentence": "Die Zielausschüttung beträgt 4.0 %.", "value": "4.0 %"}, { @@ -161,6 +170,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.NUMBER, "position": 10, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die Ausschüttung im Jahr 2024 lag bei 3.8 %.", @@ -178,6 +188,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.STRING, "position": 11, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die Laufzeit des Fonds beträgt 7 Jahre.", @@ -192,6 +203,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.NUMBER, "position": 12, "active": True, + "is_trained": True, "examples": [ {"sentence": "Der LTV beträgt 65.0 %.", "value": "65.0 %"}, {"sentence": "Loan-to-Value-Ratio: 65.0 %.", "value": "65.0 %"}, @@ -203,6 +215,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.NUMBER, "position": 13, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die Managementgebühren betragen jährlich 1.5 %.", @@ -220,6 +233,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.ARRAY, "position": 14, "active": True, + "is_trained": True, "examples": [ { "sentence": "Die Sektorenallokation umfasst Büro, Wohnen und Logistik.", @@ -237,6 +251,7 @@ def seed_default_kpi_settings(): "type": KPISettingType.ARRAY, "position": 15, "active": True, + "is_trained": True, "examples": [ { "sentence": "Investitionen erfolgen in Deutschland, Frankreich und Österreich.", @@ -260,6 +275,7 @@ def seed_default_kpi_settings(): position=kpi_data["position"], active=kpi_data["active"], examples=kpi_data.get("examples", []), + is_trained=kpi_data["is_trained"], ) db.session.add(kpi_setting) @@ -273,3 +289,4 @@ def seed_default_kpi_settings(): db.session.rollback() print(f"Fehler beim Hinzufügen der Standard KPI Settings: {e}") raise + diff --git a/project/backend/spacy-service/spacy_training/output/model-last/ner/model b/project/backend/spacy-service/spacy_training/output/model-last/ner/model index 303ffe1..4e9113e 100644 Binary files a/project/backend/spacy-service/spacy_training/output/model-last/ner/model and b/project/backend/spacy-service/spacy_training/output/model-last/ner/model differ diff --git a/project/backend/spacy-service/spacy_training/output/model-last/vocab/strings.json b/project/backend/spacy-service/spacy_training/output/model-last/vocab/strings.json index e54596f..3bee512 100644 --- a/project/backend/spacy-service/spacy_training/output/model-last/vocab/strings.json +++ b/project/backend/spacy-service/spacy_training/output/model-last/vocab/strings.json @@ -1394,7 +1394,6 @@ "across", "act", "active", - "adasd23", "add", "adv", "adv.", @@ -1684,7 +1683,7 @@ "d.h", "d.h.", "d.x", - "d23", + "d34", "dX", "dXxx.\u20ac", "d_d", @@ -1758,6 +1757,7 @@ "dr.", "drawbacks", "driven", + "dsadad34", "dte", "du", "dual", @@ -1914,6 +1914,7 @@ "e\u2019s", "f", "f.", + "f45", "fa", "fa.", "faktor", @@ -1929,6 +1930,7 @@ "festgelegt", "festgelegter", "ff", + "fh5", "fierce", "fil", "financially", @@ -2026,6 +2028,8 @@ "ggfs.", "gg\u00fc", "gg\u00fc.", + "ghgh56", + "ghghgwef45", "ght", "gic", "gie", @@ -2050,6 +2054,7 @@ "h.", "h.c", "h.c.", + "h56", "haltedauer", "halten", "halten-strategie", @@ -2073,6 +2078,7 @@ "her", "here", "hes", + "hewhfh5", "hf.", "hg", "hg.", @@ -2260,6 +2266,7 @@ "jeweiliges", "jh", "jh.", + "jhchewqc7", "jhd", "jhd.", "jor", @@ -2293,9 +2300,11 @@ "key", "kindertagesst\u00e4tte", "kingdom", + "kkn7", "kl.", "klassifikation", "klassifizierung", + "kn7", "kontinentaleuropaische", "kosten", "kt-", @@ -2693,6 +2702,7 @@ "q.", "q.e.d", "q.e.d.", + "qc7", "quality", "quarterly", "quota", @@ -2791,12 +2801,14 @@ "sa.", "sale", "sb.", + "sc3", "schule", "schweden", "scope", "scs", "scsp", "sd.", + "sdcdsc3", "sector", "sectors", "sed", @@ -3128,12 +3140,14 @@ "xxx-Xxxxx", "xxx-xxxx", "xxx.", + "xxxd", "xxxx", "xxxx+", "xxxx-xx", "xxxx-xxx", "xxxx-xxxx", "xxxx.", + "xxxxd", "xxxxdd", "xxxx\u2019x", "xxx\u2019x", diff --git a/project/frontend/src/components/ConfigTable.tsx b/project/frontend/src/components/ConfigTable.tsx index 2a9ae34..1bfcb38 100644 --- a/project/frontend/src/components/ConfigTable.tsx +++ b/project/frontend/src/components/ConfigTable.tsx @@ -6,41 +6,56 @@ import type { Kennzahl } from "../types/kpi"; import { getDisplayType } from "../types/kpi"; import { fetchKennzahlen as fetchK } from "../util/api"; import { API_HOST } from "../util/api"; +import WarningAmberIcon from "@mui/icons-material/WarningAmber"; -type ConfigTableProps = { + + + +export type ConfigTableProps = { from?: string; + trainingRunning?: boolean; }; -export function ConfigTable({ from }: ConfigTableProps) { + + +export function ConfigTable({ from, trainingRunning }: ConfigTableProps) { const navigate = useNavigate(); const [kennzahlen, setKennzahlen] = useState([]); const [draggedItem, setDraggedItem] = useState(null); const [isUpdatingPositions, setIsUpdatingPositions] = useState(false); const [loading, setLoading] = useState(true); - useEffect(() => { - const fetchKennzahlen = async () => { - while (true) { - try { - console.log("Fetching kennzahlen from API..."); - const data = await fetchK(); - console.log("Fetched kennzahlen:", data); - const sortedData = data.sort( - (a: Kennzahl, b: Kennzahl) => a.position - b.position, - ); - setKennzahlen(sortedData); - setLoading(false); - break; - } catch (err) { - console.error("Error fetching kennzahlen:", err); - await new Promise((resolve) => setTimeout(resolve, 2000)); - } + const fetchKennzahlen = async () => { + while (true) { + try { + console.log("Fetching kennzahlen from API..."); + const data = await fetchK(); + console.log("Fetched kennzahlen:", data); + const sortedData = data.sort((a, b) => a.position - b.position); + setKennzahlen(sortedData); + setLoading(false); + break; + } catch (err) { + console.error("Error fetching kennzahlen:", err); + await new Promise((resolve) => setTimeout(resolve, 2000)); } - }; + } + }; + + useEffect(() => { fetchKennzahlen(); }, []); + useEffect(() => { + if (trainingRunning === false) { + console.log("[ConfigTable] Training beendet → Kennzahlen neu laden..."); + fetchKennzahlen(); + } + }, [trainingRunning]); + + + const handleToggleActive = async (id: number) => { const kennzahl = kennzahlen.find((k) => k.id === id); if (!kennzahl) return; @@ -326,15 +341,32 @@ export function ConfigTable({ from }: ConfigTableProps) { padding: "12px", fontSize: "14px", color: "#333", + display: "flex", + alignItems: "center", + gap: "8px", }} > {kennzahl.name} - {kennzahl.mandatory && ( - * - )} + {kennzahl.mandatory && *} + {kennzahl.is_trained === false && ( + + Diese Kennzahl ist nicht trainiert.
+ Klicken Sie oben auf "Neu trainieren", um das Training zu starten. + + } + arrow + placement="right" + > + +
+ )} + + = { name: '', - description: '', mandatory: false, type: 'string', - translation: '', - example: '', active: true, examples: [{ sentence: '', value: '' }], }; @@ -115,26 +112,24 @@ export function KPIForm({ mode, initialData, onSave, onCancel, loading = false, // Dann in die DB speichern await onSave({ name: formData.name!, - description: formData.description || '', mandatory: formData.mandatory ?? false, type: formData.type || 'string', - translation: formData.translation || '', - example: formData.example || '', position: formData.position ?? 0, active: formData.active ?? true, - examples: formData.examples ?? [] + examples: formData.examples ?? [], + is_trained: false, }); // Formular zurücksetzen: setFormData(emptyKPI); - setSnackbarMessage("Beispielsätze gespeichert. Jetzt auf ‚Neu trainieren‘ klicken oder weitere Kennzahlen hinzufügen."); + setSnackbarMessage("Beispielsätze gespeichert. Jetzt auf -Neu trainieren- klicken oder weitere Kennzahlen hinzufügen."); setSnackbarSeverity("success"); setSnackbarOpen(true); } catch (e: any) { // Prüfe auf 409-Fehler if (e?.message?.includes("409") || e?.response?.status === 409) { - setSnackbarMessage("Diese Kennzahl existiert bereits. Sie können sie unter ‚Konfiguration‘ bearbeiten."); + setSnackbarMessage("Diese Kennzahl existiert bereits. Sie können sie unter -Konfiguration- bearbeiten."); setSnackbarSeverity("info"); setSnackbarOpen(true); } else { diff --git a/project/frontend/src/routes/config.tsx b/project/frontend/src/routes/config.tsx index ae32e9a..8bee3e7 100644 --- a/project/frontend/src/routes/config.tsx +++ b/project/frontend/src/routes/config.tsx @@ -165,12 +165,16 @@ function ConfigPage() { }} > {trainingRunning ? ( - + <> + + Wird trainiert... + ) : ( "Neu trainieren" )} +