Neu trainieren-Button deaktiviert, wenn keine neuen Kennzahlen vorhanden sind; Trainingsdaten werden beim Start bereinigt

pull/94/head
Abdulrahman Dabbagh 2025-06-29 16:34:25 +02:00
parent 521ac1dcbd
commit 5af1d40c08
6 changed files with 90 additions and 186 deletions

View File

@ -1,11 +1,12 @@
from controller.spacy_controller import spacy_controller from controller.spacy_controller import spacy_controller
from controller.kpi_setting_controller import kpi_setting_controller from controller.kpi_setting_controller import kpi_setting_controller, kpi_routes
from controller.pitch_book_controller import pitch_book_controller from controller.pitch_book_controller import pitch_book_controller
from controller.progress_controller import progress_controller from controller.progress_controller import progress_controller
def register_routes(app): def register_routes(app):
app.register_blueprint(kpi_setting_controller) app.register_blueprint(kpi_setting_controller)
app.register_blueprint(kpi_routes)
app.register_blueprint(pitch_book_controller) app.register_blueprint(pitch_book_controller)
app.register_blueprint(spacy_controller) app.register_blueprint(spacy_controller)
app.register_blueprint(progress_controller) app.register_blueprint(progress_controller)

View File

@ -2,7 +2,10 @@ from flask import Blueprint, request, jsonify
from model.database import db from model.database import db
from model.kpi_setting_model import KPISettingModel, KPISettingType from model.kpi_setting_model import KPISettingModel, KPISettingType
# Routen für /api/kpi/settings (Auslesen im Frontend)
kpi_routes = Blueprint("kpi_routes", __name__, url_prefix="/api/kpi")
# Routen für /api/kpi_setting/ (Hinzufügen, Ändern, Löschen)
kpi_setting_controller = Blueprint( kpi_setting_controller = Blueprint(
"kpi_settings", __name__, url_prefix="/api/kpi_setting" "kpi_settings", __name__, url_prefix="/api/kpi_setting"
) )
@ -14,12 +17,6 @@ def get_all_kpi_settings():
return jsonify([kpi_setting.to_dict() for kpi_setting in kpi_settings]), 200 return jsonify([kpi_setting.to_dict() for kpi_setting in kpi_settings]), 200
@kpi_setting_controller.route("/<int:id>", methods=["GET"])
def get_kpi_setting(id):
kpi_setting = KPISettingModel.query.get_or_404(id)
return jsonify(kpi_setting.to_dict()), 200
@kpi_setting_controller.route("/", methods=["POST"]) @kpi_setting_controller.route("/", methods=["POST"])
def create_kpi_setting(): def create_kpi_setting():
data = request.json data = request.json
@ -149,3 +146,21 @@ def update_kpi_positions():
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
return jsonify({"error": f"Failed to update positions: {str(e)}"}), 500 return jsonify({"error": f"Failed to update positions: {str(e)}"}), 500
@kpi_routes.route("/settings", methods=["GET"])
def get_kpi_settings():
try:
kpis = KPISettingModel.query.all()
return jsonify([k.to_dict() for k in kpis]), 200
except Exception as e:
return (
jsonify({"error": "Fehler beim Abrufen der KPIs", "details": str(e)}),
500,
)
@kpi_setting_controller.route("/<int:id>", methods=["GET"])
def get_kpi_setting(id):
kpi_setting = KPISettingModel.query.get_or_404(id)
return jsonify(kpi_setting.to_dict()), 200

View File

@ -519,16 +519,6 @@
] ]
] ]
}, },
{
"text": "Laufzeit / Investtionszeltraum,10 bis 12 Jahre / bis zu 24 Monate angestrebt Ausschüttungsintervalle,Mindestens jährlich",
"entities": [
[
31,
46,
"LAUFZEIT"
]
]
},
{ {
"text": "10-12 Jahre Laufzeit bei einem LTV von bis zu 20%", "text": "10-12 Jahre Laufzeit bei einem LTV von bis zu 20%",
"entities": [ "entities": [
@ -594,21 +584,6 @@
] ]
] ]
}, },
{
"text": "Vehicle / domicile Alternative Investment Fund / Luxembourg (e.g. SCSp SICAV-RAIF) Investment strategy eturn pro Real Estate (PropCo + OpCo) Investing in upscale hotels with long-term management contracts in major European destinations Core/Core+ with OpCo premium Management Agreements solely with financially strong and experienced partners/ global brands Cash flow-oriented Cash-flow pattern Target equity /AuM € 400m equity / € 800m AuM (50% Loan-to-Value) Vehicle lifetime / investment period Open-ended fund",
"entities": [
[
498,
513,
"LAUFZEIT"
],
[
236,
245,
"RISIKOPROFIL"
]
]
},
{ {
"text": "Vehicle type (Lux-RAIF) (net of fees) IRR6.5% ACCOR Vehicle structure Open-ended Targetvehiclesize € 400m (equity) Manager-defined Core/Core+ with | style OpCo Premium darge CLV. 50% Pt H | LTO N WORLDWIDE Year of first closing 2020 Target no. ofinvestors 1-5 Fund life (yrs} Open-ended Min-commitmentper —¢ 400m", "text": "Vehicle type (Lux-RAIF) (net of fees) IRR6.5% ACCOR Vehicle structure Open-ended Targetvehiclesize € 400m (equity) Manager-defined Core/Core+ with | style OpCo Premium darge CLV. 50% Pt H | LTO N WORLDWIDE Year of first closing 2020 Target no. ofinvestors 1-5 Fund life (yrs} Open-ended Min-commitmentper —¢ 400m",
"entities": [ "entities": [
@ -729,31 +704,6 @@
] ]
] ]
}, },
{
"text": "Prognose: 7,5%+ IRR auf Fondsebene",
"entities": [
[
10,
14,
"RENDITE"
]
]
},
{
"text": "= Prognostizierte jährliche Ausschüttung* von 84,0% = Prognostizierte Gesamtrendite (IRR}* von 7,5%",
"entities": [
[
96,
100,
"RENDITE"
],
[
49,
53,
"AUSSCHÜTTUNGSRENDITE"
]
]
},
{ {
"text": "= Lagefokussierung: Metropolregionen Deutschlands = Finanzierung: max. 20% LTV = Risikoprofil: Core, Core +", "text": "= Lagefokussierung: Metropolregionen Deutschlands = Finanzierung: max. 20% LTV = Risikoprofil: Core, Core +",
"entities": [ "entities": [
@ -1054,36 +1004,6 @@
] ]
] ]
}, },
{
"text": "Unternehmensimmobilien Club 1 2016 Light Industrial Core+/D € 186 Mio. 9 (voll investiert) 13,0 % p.a.",
"entities": [
[
52,
57,
"RISIKOPROFIL"
],
[
91,
97,
"RENDITE"
]
]
},
{
"text": "Unternehmensimmobilien Club 2 2021 Light Industrial Core+/D € 262 Mio. 12 (voll investiert) 9,00 % p.a.",
"entities": [
[
52,
57,
"RISIKOPROFIL"
],
[
92,
98,
"RENDITE"
]
]
},
{ {
"text": "Individualmandat 2022 Light Industrial Value-Add / Nordics € 100 Mio. 5 (voll investiert) 18,0 % p.a.", "text": "Individualmandat 2022 Light Industrial Value-Add / Nordics € 100 Mio. 5 (voll investiert) 18,0 % p.a.",
"entities": [ "entities": [

View File

@ -127,13 +127,10 @@
"/080%/212%/491", "/080%/212%/491",
"/2,12", "/2,12",
"/3", "/3",
"/AuM",
"/Core+", "/Core+",
"/FK", "/FK",
"/XX", "/XX",
"/XxX",
"/Xxxx+", "/Xxxx+",
"/aum",
"/core+", "/core+",
"/d", "/d",
"/d,dd", "/d,dd",
@ -276,7 +273,6 @@
"45", "45",
"491", "491",
"5", "5",
"5%+",
"5,0", "5,0",
"5,00", "5,00",
"5,1", "5,1",
@ -306,7 +302,6 @@
"7", "7",
"7,1", "7,1",
"7,5", "7,5",
"7,5%+",
"7,50", "7,50",
"7,50%+", "7,50%+",
"7.5", "7.5",
@ -323,8 +318,6 @@
"8-D", "8-D",
"8-d", "8-d",
"80", "80",
"800",
"84,0",
"85", "85",
"8D", "8D",
"8d", "8d",
@ -464,7 +457,6 @@
"Abteilung", "Abteilung",
"Access", "Access",
"Add", "Add",
"Agreements",
"Aktive", "Aktive",
"Aktueller", "Aktueller",
"AlF", "AlF",
@ -472,7 +464,6 @@
"Allocation", "Allocation",
"Allokation", "Allokation",
"Allokationsprofil", "Allokationsprofil",
"Alternative",
"Amsterdam", "Amsterdam",
"Andere", "Andere",
"Anfrage", "Anfrage",
@ -508,7 +499,6 @@
"Artikel", "Artikel",
"Asset", "Asset",
"Assets", "Assets",
"AuM",
"Aufbau", "Aufbau",
"Auflage", "Auflage",
"Aufl\u00f6sung", "Aufl\u00f6sung",
@ -578,10 +568,7 @@
"COR", "COR",
"CORE", "CORE",
"CSU", "CSU",
"CSp",
"Cash",
"Cash-Flow-Stabilit\u00e4t", "Cash-Flow-Stabilit\u00e4t",
"Cash-flow",
"Chr", "Chr",
"Chr.", "Chr.",
"Cie", "Cie",
@ -815,7 +802,6 @@
"Interner", "Interner",
"Invastitionsfokus", "Invastitionsfokus",
"Investftionsvolumen", "Investftionsvolumen",
"Investing",
"Investitionen", "Investitionen",
"Investitions-annahmen", "Investitions-annahmen",
"Investitionsphase", "Investitionsphase",
@ -825,7 +811,6 @@
"Investmentzeitraum", "Investmentzeitraum",
"Investoren", "Investoren",
"Investtionszeltraum", "Investtionszeltraum",
"Investtionszeltraum,10",
"Investtonszeltraum", "Investtonszeltraum",
"Ireland", "Ireland",
"Irland", "Irland",
@ -897,7 +882,6 @@
"Light", "Light",
"Limited", "Limited",
"Lisbon", "Lisbon",
"Loan-to-Value",
"Local", "Local",
"Logistics", "Logistics",
"Logistik", "Logistik",
@ -1048,12 +1032,10 @@
"Prof", "Prof",
"Prof.", "Prof.",
"Professor", "Professor",
"Prognose",
"Prognostiderte", "Prognostiderte",
"Prognostizierte", "Prognostizierte",
"Projektentwicklungen", "Projektentwicklungen",
"Projektentwicklungsrisiken", "Projektentwicklungsrisiken",
"PropCo",
"Pt", "Pt",
"Punkt", "Punkt",
"Q", "Q",
@ -1095,7 +1077,6 @@
"S", "S",
"S'", "S'",
"SCS", "SCS",
"SCSp",
"SEKTORENALLOKATION", "SEKTORENALLOKATION",
"SFDR", "SFDR",
"SG-", "SG-",
@ -1254,7 +1235,6 @@
"XXXX", "XXXX",
"XXXX-XXXX", "XXXX-XXXX",
"XXXd.d", "XXXd.d",
"XXXx",
"XXXxx", "XXXxx",
"XXx", "XXx",
"XXxxxx", "XXxxxx",
@ -1279,18 +1259,15 @@
"Xxxx-XXX", "Xxxx-XXX",
"Xxxx-Xxxx-Xxxxx", "Xxxx-Xxxx-Xxxxx",
"Xxxx-Xxxxx-XXX", "Xxxx-Xxxxx-XXX",
"Xxxx-xx-Xxxxx",
"Xxxx-xxx", "Xxxx-xxx",
"Xxxx-xxxx", "Xxxx-xxxx",
"Xxxx.", "Xxxx.",
"Xxxx.-Xxx", "Xxxx.-Xxx",
"Xxxx.-Xxx.", "Xxxx.-Xxx.",
"XxxxXx",
"Xxxxx", "Xxxxx",
"Xxxxx)-", "Xxxxx)-",
"Xxxxx)/Xxxx", "Xxxxx)/Xxxx",
"Xxxxx+", "Xxxxx+",
"Xxxxx,dd",
"Xxxxx-", "Xxxxx-",
"Xxxxx-XXX", "Xxxxx-XXX",
"Xxxxx-XxX", "Xxxxx-XxX",
@ -1371,6 +1348,7 @@
"a.g.", "a.g.",
"a.m.", "a.m.",
"a.z.", "a.z.",
"a34",
"ab", "ab",
"abb", "abb",
"abb.", "abb.",
@ -1401,7 +1379,6 @@
"ae", "ae",
"aft", "aft",
"age", "age",
"agreements",
"aha", "aha",
"ahe", "ahe",
"ahl", "ahl",
@ -1427,7 +1404,6 @@
"allokationsprofil", "allokationsprofil",
"als", "als",
"also", "also",
"alternative",
"aly", "aly",
"am.", "am.",
"ambulant", "ambulant",
@ -1483,7 +1459,6 @@
"ary", "ary",
"as", "as",
"ase", "ase",
"ash",
"ass", "ass",
"asset", "asset",
"assetor", "assetor",
@ -1529,7 +1504,6 @@
"b.sc", "b.sc",
"b.sc.", "b.sc.",
"bahnhof", "bahnhof",
"bal",
"balanced", "balanced",
"basis", "basis",
"bau", "bau",
@ -1571,7 +1545,6 @@
"both", "both",
"bps", "bps",
"br.", "br.",
"brands",
"broad", "broad",
"brussels", "brussels",
"bruttofondsverm\u00f6gens", "bruttofondsverm\u00f6gens",
@ -1604,8 +1577,6 @@
"capital", "capital",
"capped", "capped",
"carbon", "carbon",
"cash",
"cash-flow",
"cash-flow-stabilit\u00e4t", "cash-flow-stabilit\u00e4t",
"cbd", "cbd",
"cdu", "cdu",
@ -1666,7 +1637,6 @@
"d+au", "d+au",
"d+aut", "d+aut",
"d,d", "d,d",
"d,d%+",
"d,dd", "d,dd",
"d,dd%+", "d,dd%+",
"d,ddd", "d,ddd",
@ -1683,7 +1653,6 @@
"d.h", "d.h",
"d.h.", "d.h.",
"d.x", "d.x",
"d34",
"dX", "dX",
"dXxx.\u20ac", "dXxx.\u20ac",
"d_d", "d_d",
@ -1693,6 +1662,7 @@
"darge", "darge",
"darlehen", "darlehen",
"das", "das",
"dasda34",
"dat", "dat",
"dd", "dd",
"dd,d", "dd,d",
@ -1718,7 +1688,6 @@
"der", "der",
"dergleichen", "dergleichen",
"des", "des",
"destinations",
"deutsche", "deutsche",
"deutsches", "deutsches",
"deutschland", "deutschland",
@ -1749,7 +1718,6 @@
"dle", "dle",
"do", "do",
"do.", "do.",
"domicile",
"domiciled", "domiciled",
"don", "don",
"down", "down",
@ -1757,7 +1725,6 @@
"dr.", "dr.",
"drawbacks", "drawbacks",
"driven", "driven",
"dsadad34",
"dte", "dte",
"du", "du",
"dual", "dual",
@ -1828,7 +1795,6 @@
"eln", "eln",
"els", "els",
"elt", "elt",
"ely",
"em", "em",
"em.", "em.",
"emerging", "emerging",
@ -1882,7 +1848,6 @@
"ete", "ete",
"etr", "etr",
"ets", "ets",
"eturn",
"eu-offenlegungsverordnung", "eu-offenlegungsverordnung",
"eur", "eur",
"euro", "euro",
@ -1914,7 +1879,6 @@
"e\u2019s", "e\u2019s",
"f", "f",
"f.", "f.",
"f45",
"fa", "fa",
"fa.", "fa.",
"faktor", "faktor",
@ -1930,10 +1894,8 @@
"festgelegt", "festgelegt",
"festgelegter", "festgelegter",
"ff", "ff",
"fh5",
"fierce", "fierce",
"fil", "fil",
"financially",
"finanzierung", "finanzierung",
"finanzierungskonditionen", "finanzierungskonditionen",
"finland", "finland",
@ -1941,7 +1903,6 @@
"first", "first",
"flagship", "flagship",
"flow", "flow",
"flow-oriented",
"fl\u00e4che", "fl\u00e4che",
"focus", "focus",
"focused", "focused",
@ -2028,13 +1989,10 @@
"ggfs.", "ggfs.",
"gg\u00fc", "gg\u00fc",
"gg\u00fc.", "gg\u00fc.",
"ghgh56",
"ghghgwef45",
"ght", "ght",
"gic", "gic",
"gie", "gie",
"gl.", "gl.",
"global",
"globale", "globale",
"gmbh", "gmbh",
"goal", "goal",
@ -2054,7 +2012,6 @@
"h.", "h.",
"h.c", "h.c",
"h.c.", "h.c.",
"h56",
"haltedauer", "haltedauer",
"halten", "halten",
"halten-strategie", "halten-strategie",
@ -2078,7 +2035,6 @@
"her", "her",
"here", "here",
"hes", "hes",
"hewhfh5",
"hf.", "hf.",
"hg", "hg",
"hg.", "hg.",
@ -2216,7 +2172,6 @@
"investoren", "investoren",
"investors", "investors",
"investtionszeltraum", "investtionszeltraum",
"investtionszeltraum,10",
"investtonszeltraum", "investtonszeltraum",
"inw", "inw",
"io.", "io.",
@ -2266,7 +2221,6 @@
"jeweiliges", "jeweiliges",
"jh", "jh",
"jh.", "jh.",
"jhchewqc7",
"jhd", "jhd",
"jhd.", "jhd.",
"jor", "jor",
@ -2300,11 +2254,9 @@
"key", "key",
"kindertagesst\u00e4tte", "kindertagesst\u00e4tte",
"kingdom", "kingdom",
"kkn7",
"kl.", "kl.",
"klassifikation", "klassifikation",
"klassifizierung", "klassifizierung",
"kn7",
"kontinentaleuropaische", "kontinentaleuropaische",
"kosten", "kosten",
"kt-", "kt-",
@ -2362,8 +2314,6 @@
"lls", "lls",
"llt", "llt",
"llv", "llv",
"lly",
"loan-to-value",
"local", "local",
"locations", "locations",
"lock-in", "lock-in",
@ -2371,7 +2321,6 @@
"logistik", "logistik",
"logistikimmobilien", "logistikimmobilien",
"london", "london",
"long-term",
"low", "low",
"lps", "lps",
"lso", "lso",
@ -2644,9 +2593,7 @@
"parformanceabh\u00e4ngige", "parformanceabh\u00e4ngige",
"paris", "paris",
"parks", "parks",
"partners",
"partnership", "partnership",
"pattern",
"ped", "ped",
"pen", "pen",
"per", "per",
@ -2677,18 +2624,15 @@
"pricey", "pricey",
"pricing", "pricing",
"prime", "prime",
"pro",
"prof", "prof",
"prof.", "prof.",
"profile", "profile",
"prognose",
"prognostiderte", "prognostiderte",
"prognostizierte", "prognostizierte",
"program", "program",
"projects", "projects",
"projektentwicklungen", "projektentwicklungen",
"projektentwicklungsrisiken", "projektentwicklungsrisiken",
"propco",
"properties", "properties",
"proprietary", "proprietary",
"provide", "provide",
@ -2702,7 +2646,6 @@
"q.", "q.",
"q.e.d", "q.e.d",
"q.e.d.", "q.e.d.",
"qc7",
"quality", "quality",
"quarterly", "quarterly",
"quota", "quota",
@ -2801,14 +2744,11 @@
"sa.", "sa.",
"sale", "sale",
"sb.", "sb.",
"sc3",
"schule", "schule",
"schweden", "schweden",
"scope", "scope",
"scs", "scs",
"scsp",
"sd.", "sd.",
"sdcdsc3",
"sector", "sector",
"sectors", "sectors",
"sed", "sed",
@ -2849,7 +2789,6 @@
"sofern", "sofern",
"sog", "sog",
"sog.", "sog.",
"solely",
"solvency", "solvency",
"some", "some",
"son", "son",
@ -3140,14 +3079,12 @@
"xxx-Xxxxx", "xxx-Xxxxx",
"xxx-xxxx", "xxx-xxxx",
"xxx.", "xxx.",
"xxxd",
"xxxx", "xxxx",
"xxxx+", "xxxx+",
"xxxx-xx", "xxxx-xx",
"xxxx-xxx", "xxxx-xxx",
"xxxx-xxxx", "xxxx-xxxx",
"xxxx.", "xxxx.",
"xxxxd",
"xxxxdd", "xxxxdd",
"xxxx\u2019x", "xxxx\u2019x",
"xxx\u2019x", "xxx\u2019x",

View File

@ -11,6 +11,7 @@ import CircularProgress from "@mui/material/CircularProgress";
export const Route = createFileRoute("/config")({ export const Route = createFileRoute("/config")({
component: ConfigPage, component: ConfigPage,
validateSearch: (search: Record<string, unknown>): { from?: string; success?: string } => { validateSearch: (search: Record<string, unknown>): { from?: string; success?: string } => {
@ -28,6 +29,28 @@ function ConfigPage() {
const [snackbarOpen, setSnackbarOpen] = useState(success === "true"); const [snackbarOpen, setSnackbarOpen] = useState(success === "true");
const [snackbarMessage, setSnackbarMessage] = useState<string>("Beispielsätze gespeichert. Jetzt auf -Neu trainieren- klicken oder zuerst weitere Kennzahlen hinzufügen."); const [snackbarMessage, setSnackbarMessage] = useState<string>("Beispielsätze gespeichert. Jetzt auf -Neu trainieren- klicken oder zuerst weitere Kennzahlen hinzufügen.");
const [trainingRunning, setTrainingRunning] = useState(false); const [trainingRunning, setTrainingRunning] = useState(false);
const [hasUntrainedKPIs, setHasUntrainedKPIs] = useState(false);
const fetchKPISettings = async () => {
try {
const res = await fetch(`${API_HOST}/api/kpi/settings`);
const data = await res.json();
console.log("🔍 GELADENE KPIs:", data);
const untrainedExists = data.some((kpi: any) => {
console.log("➡️", kpi.name, "is_trained:", kpi.is_trained);
return kpi.is_trained === false;
});
setHasUntrainedKPIs(untrainedExists);
} catch (err) {
console.error("Fehler beim Laden der KPIs:", err);
}
};
useEffect(() => {
fetchKPISettings();
}, []);
@ -61,8 +84,7 @@ function ConfigPage() {
checkInitialTrainingStatus(); checkInitialTrainingStatus();
}, []); }, []);
const handleAddNewKPI = () => { const handleAddNewKPI = () => {
navigate({ navigate({
to: "/config-add", to: "/config-add",
@ -113,8 +135,11 @@ function ConfigPage() {
console.log("Training abgeschlossen Snackbar wird ausgelöst"); console.log("Training abgeschlossen Snackbar wird ausgelöst");
setSnackbarMessage("Training abgeschlossen!"); setSnackbarMessage("Training abgeschlossen!");
setSnackbarOpen(true); setSnackbarOpen(true);
setTrainingRunning(false); setTrainingRunning(false);
fetchKPISettings(); // 👉 hier hinzufügen!
} }
} catch (err) { } catch (err) {
console.error("Polling-Fehler:", err); console.error("Polling-Fehler:", err);
@ -152,40 +177,46 @@ function ConfigPage() {
Konfiguration der Kennzahlen Konfiguration der Kennzahlen
</Typography> </Typography>
</Box> </Box>
<Box display="flex" flexDirection="column" alignItems="flex-end" gap={1}>
<Box display="flex" gap={2}>
<Button
variant="contained"
onClick={handleTriggerTraining}
disabled={trainingRunning || !hasUntrainedKPIs}
sx={{
backgroundColor: "#383838",
"&:hover": { backgroundColor: "#2e2e2e" },
}}
>
{trainingRunning ? (
<>
<CircularProgress size={20} sx={{ color: "white", mr: 1 }} />
Wird trainiert...
</>
) : (
"Neu trainieren"
)}
</Button>
{/* Rechte Seite: Buttons */} <Button
<Box display="flex" gap={2}> variant="contained"
<Button onClick={handleAddNewKPI}
variant="contained" sx={{
onClick={handleTriggerTraining} backgroundColor: "#383838",
disabled={trainingRunning} "&:hover": { backgroundColor: "#2e2e2e" },
sx={{ }}
backgroundColor: "#383838", >
"&:hover": { backgroundColor: "#2e2e2e" }, Neue Kennzahl hinzufügen
}} </Button>
> </Box>
{trainingRunning ? (
<>
<CircularProgress size={20} sx={{ color: "white", mr: 1 }} />
Wird trainiert...
</>
) : (
"Neu trainieren"
)}
</Button>
{!hasUntrainedKPIs && (
<Button <Typography variant="body2" color="text.secondary" mt={1}>
variant="contained" Alle Kennzahlen sind bereits trainiert.
onClick={handleAddNewKPI} </Typography>
sx={{ )}
backgroundColor: "#383838",
"&:hover": { backgroundColor: "#2e2e2e" },
}}
>
Neue Kennzahl hinzufügen
</Button>
</Box> </Box>
</Box> </Box>
{/* Tabelle */} {/* Tabelle */}