diff --git a/go_server/src/utils.go b/go_server/src/utils.go index f5616e1c..750df4bb 100644 --- a/go_server/src/utils.go +++ b/go_server/src/utils.go @@ -239,7 +239,8 @@ func copyOutput(r io.Reader, response *string) { // ReadFile reads a file and returns its content as a string func ReadFile(filename string) string { - absPath, _ := filepath.Abs(filename) + cleanPath := strings.Trim(filename, "\" \t\n\r") + absPath, _ := filepath.Abs(cleanPath) log.Println("Reading file: " + absPath) data, err := os.ReadFile(absPath) if err != nil { diff --git a/pythonCode/med_libs/MEDml/nodes/ModelHandler.py b/pythonCode/med_libs/MEDml/nodes/ModelHandler.py index 85ae8f1a..290453cc 100644 --- a/pythonCode/med_libs/MEDml/nodes/ModelHandler.py +++ b/pythonCode/med_libs/MEDml/nodes/ModelHandler.py @@ -1,3 +1,4 @@ +import ast import copy import json from typing import Union @@ -131,6 +132,7 @@ def __calculate_all_metrics(self, y_true, y_pred, y_pred_proba=None): metrics['MCC'] = round(matthews_corrcoef(y_true, y_pred), 3) except Exception as e: + raise ValueError(f"Error calculating metrics: {e}") print(f"Error calculating metrics: {e}") # Set default values for all metrics default_metrics = ["AUC", "Sensitivity", "Specificity", "PPV", "NPV", "Accuracy", "F1", "MCC"] @@ -145,7 +147,7 @@ def __calculate_overall_metrics(self, fold_metrics): log_metrics = {} if not fold_metrics: - return overall_metrics + return overall_metrics, log_metrics # Get all metric names from first fold first_fold_metrics = list(fold_metrics.values())[0] @@ -401,7 +403,7 @@ def __custom_train_and_evaluate( if self.optimize_threshold: if len(pycaret_exp.get_config('y').unique()) == 2 and not self.ensembleEnabled: self.CodeHandler.add_line("code", f"# Optimizing model threshold based on {self.threshold_optimization_metric}", indent=0) - self.CodeHandler.add_line("code", f"best_model = pycaret_exp.optimize_threshold(best_model, metric='{self.threshold_optimization_metric}')", indent=0) + self.CodeHandler.add_line("code", f"best_model = pycaret_exp.optimize_threshold(best_model, optimize='{self.threshold_optimization_metric}')", indent=0) # Finalize the model if finalize: @@ -552,7 +554,7 @@ def __handle_splitted_data(self, experiment: dict, settings: dict, **kwargs) -> ) self.CodeHandler.add_line( "code", - f"trained_models = [pycaret_exp.optimize_threshold(trained_models[0], metric='{self.threshold_optimization_metric}')]" + f"trained_models = [pycaret_exp.optimize_threshold(trained_models[0], optimize='{self.threshold_optimization_metric}')]" ) if finalize: @@ -756,7 +758,7 @@ def _execute(self, experiment: dict = None, **kwargs) -> json: else: trained_models = [experiment['pycaret_exp'].optimize_threshold(trained_models[0], optimize=self.threshold_optimization_metric)] - self.CodeHandler.add_line("code", f"trained_models = [pycaret_exp.optimize_threshold(trained_models[0], metric='{self.threshold_optimization_metric}')]") + self.CodeHandler.add_line("code", f"trained_models = [pycaret_exp.optimize_threshold(trained_models[0], optimize='{self.threshold_optimization_metric}')]") if finalize: trained_models = [experiment['pycaret_exp'].finalize_model(model) for model in trained_models] diff --git a/pythonCode/med_libs/MEDml/nodes/ModelIO.py b/pythonCode/med_libs/MEDml/nodes/ModelIO.py index 8f1345e7..a820eaf6 100644 --- a/pythonCode/med_libs/MEDml/nodes/ModelIO.py +++ b/pythonCode/med_libs/MEDml/nodes/ModelIO.py @@ -58,13 +58,14 @@ def _execute(self, experiment: dict = None, **kwargs) -> json: if dir(fitted_model).__contains__('feature_names_in_'): model_features = fitted_model.__getattribute__('feature_names_in_') elif dir(fitted_model).__contains__('feature_name_') and model_features is None: - model_features = fitted_model.__getattribute__('feature_name_') + model_features = fitted_model.__getattribute__('feature_names_in_') + elif dir(fitted_model).__contains__('classifier_') and dir(fitted_model.classifier_).__contains__('feature_names_in_'): + model_features = fitted_model.classifier_.feature_names_in_ else: - model_features= fitted_model.__getattr__('feature_names_in_') - + raise ValueError(f"Could not find model features. Model attributes : {dir(fitted_model)}, model type: {type(fitted_model)}") if model_features is None: - raise ValueError(f"Could not find model features. Model attributes : {dir(fitted_model)}, model type: {type(fitted_model)}, model features: {model_features}") - + raise ValueError(f"Could not find model features. Model attributes : {dir(fitted_model)}, model type: {type(fitted_model)}") + model_features = list(model_features) # Model's name diff --git a/pythonCode/med_libs/MEDml/nodes/Split.py b/pythonCode/med_libs/MEDml/nodes/Split.py index 85ff9972..27efc7d0 100644 --- a/pythonCode/med_libs/MEDml/nodes/Split.py +++ b/pythonCode/med_libs/MEDml/nodes/Split.py @@ -269,7 +269,7 @@ def _execute(self, experiment: dict = None, **kwargs) -> json: splitter = StratifiedKFold(n_splits=cv_folds, shuffle=True, random_state=random_state) self.CodeHandler.add_line("code", f"splitter = StratifiedKFold(n_splits={cv_folds}, shuffle=True, random_state={random_state})") fold_iter = splitter.split(np.zeros(n_samples), y) - self.CodeHandler.add_line("code", f"fold_iter = splitter.split(np.zeros({n_samples}), y)") + self.CodeHandler.add_line("code", f"fold_iter = splitter.split(np.zeros(len(dataset)), y)") else: splitter = KFold(n_splits=cv_folds, shuffle=True, random_state=random_state) self.CodeHandler.add_line("code", f"splitter = KFold(n_splits={cv_folds}, shuffle=True, random_state={random_state})") diff --git a/pythonCode/med_libs/MEDml/utils/settings_generator/requirements.txt b/pythonCode/med_libs/MEDml/utils/settings_generator/requirements.txt index 7c59e7bf..d34e01d5 100644 Binary files a/pythonCode/med_libs/MEDml/utils/settings_generator/requirements.txt and b/pythonCode/med_libs/MEDml/utils/settings_generator/requirements.txt differ diff --git a/pythonCode/modules/superset/SupersetEnvManager.py b/pythonCode/modules/superset/SupersetEnvManager.py index d3cdc85f..31c693a7 100644 --- a/pythonCode/modules/superset/SupersetEnvManager.py +++ b/pythonCode/modules/superset/SupersetEnvManager.py @@ -41,7 +41,21 @@ def _get_build_env(self): """ env = os.environ.copy() - if sys.platform != "win32": + if sys.platform == "darwin": + # Force the compiler to use the system SDK and headers + cc = shutil.which("clang") or shutil.which("gcc") + cxx = shutil.which("clang++") or shutil.which("g++") + if cc: + env["CC"] = cc + if cxx: + env["CXX"] = cxx + + # Critical for 'cryptography' and 'python-geohash' compilation + env["LDFLAGS"] = "-L/usr/local/opt/openssl/lib -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + env["CPPFLAGS"] = "-I/usr/local/opt/openssl/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include" + # Prevents error: 'implicit declaration of function' during geohash build + env["CFLAGS"] = "-Wno-error=implicit-function-declaration" + elif sys.platform != "win32": cc = shutil.which("gcc") or shutil.which("cc") cxx = shutil.which("g++") or shutil.which("c++") if cc: diff --git a/pythonEnv/requirements.txt b/pythonEnv/requirements.txt index fc3cc7af..af2c490a 100644 --- a/pythonEnv/requirements.txt +++ b/pythonEnv/requirements.txt @@ -243,7 +243,6 @@ safetensors==0.4.0 schemdraw==0.15 scikit-base==0.5.2 scikit-image==0.19.3 -scikit-learn==1.2.0 scikit-plot==0.3.7 scipy==1.10.1 seaborn==0.12.2 diff --git a/renderer/components/learning/nodesTypes/splitNode.jsx b/renderer/components/learning/nodesTypes/splitNode.jsx index e49729bd..c75bb545 100644 --- a/renderer/components/learning/nodesTypes/splitNode.jsx +++ b/renderer/components/learning/nodesTypes/splitNode.jsx @@ -254,7 +254,7 @@ const SplitNode = ({ id, data }) => { key={id} id={id} data={data} - color="#EAD196" + color="#cea037" setupParam={data.setupParam} nodeLink="/documentation/split" defaultSettings={ diff --git a/renderer/components/learning/nodesTypes/trainModelNode.jsx b/renderer/components/learning/nodesTypes/trainModelNode.jsx index 5f06a804..a5c3ba05 100644 --- a/renderer/components/learning/nodesTypes/trainModelNode.jsx +++ b/renderer/components/learning/nodesTypes/trainModelNode.jsx @@ -30,11 +30,11 @@ const TrainModelNode = ({ id, data }) => { const [modalShowTuning, setModalShowTuning] = useState(false) const { updateNode } = useContext(FlowFunctionsContext) const [IntegrateTuning, setIntegrateTuning] = useState(data.internal.isTuningEnabled ?? false) - const [optimizeThresh, setOptimizeThresh] = useState(data.internal.isOptimizeThreshold ?? false) - const [ensembleEnabled, setEnsembleEnabled] = useState(data.internal.settings.isEnsembleEnabled ?? false) - const [calibrateEnabled, setCalibrateEnabled] = useState(data.internal.settings.isCalibrateEnabled ?? false) + const [optimizeThresh, setOptimizeThresh] = useState(data.internal.optimizeThreshold ?? false) + const [ensembleEnabled, setEnsembleEnabled] = useState(data.internal.ensembleEnabled ?? false) + const [calibrateEnabled, setCalibrateEnabled] = useState(data.internal.calibrateEnabled ?? false) - // Check if isTuningEnabled exists in data.internal, if not initialize it + // Check if default settings exists in data.internal, if not initialize it useEffect(() => { let hasUpdates = false @@ -58,8 +58,9 @@ const TrainModelNode = ({ id, data }) => { const defaults = { isTuningEnabled: false, useTuningGrid: false, - isEnsembleEnabled: false, - isCalibrateEnabled: false, + ensembleEnabled: false, + calibrateEnabled: false, + optimizeThreshold: false, settingsEnsembling: {}, settingsCalibration: {} } @@ -185,21 +186,6 @@ const TrainModelNode = ({ id, data }) => { }) } - /** - * - * @param {Object} e the event of the checkbox - * @description - * This function is used to handle the checkbox for enabling the tuning - */ - const handleIntegration = (e) => { - setIntegrateTuning(e.value) - data.internal.isTuningEnabled = e.value - updateNode({ - id: id, - updatedData: data.internal - }) - } - return ( <> {/* build on top of the Node component */} diff --git a/renderer/components/learning/results/utilities/parameters.jsx b/renderer/components/learning/results/utilities/parameters.jsx index b84c65ab..7d99d51f 100644 --- a/renderer/components/learning/results/utilities/parameters.jsx +++ b/renderer/components/learning/results/utilities/parameters.jsx @@ -15,19 +15,45 @@ import { Column } from "primereact/column" const Parameters = ({ params, tableProps, columnNames }) => { const [data, setData] = useState([]) const [selectedRows, setSelectedRows] = useState([]) + + const isEmptyOrNull = (value) => { + // Check specifically for null or undefined + if (value == null) { // Using loose equality (==) checks for both null and undefined + return true + } + + // Check if the value is an object (but not null, which typeof also calls "object") + if (typeof value === 'object') { + // A robust check for an empty object: ensure its constructor is Object and it has no own properties + return Object.keys(value).length === 0 && value.constructor === Object + } + + // Other non-object, non-null values (like strings, numbers, booleans) + // are not considered "empty objects" or "null" by this definition. + return false + } + useEffect(() => { if (params) { let dataList = [] Object.keys(params).forEach((key) => { - let value = params[key] - // For array values - if (Array.isArray(value)) { - value = JSON.stringify(value) + // skip null or undefined values + if (isEmptyOrNull(params[key])) { + dataList.push({ + param: key, + Value: "null" + }) + } else { + let value = params[key] + // For array values + if (Array.isArray(value)) { + value = JSON.stringify(value) + } + dataList.push({ + param: key, + Value: value != null ? value : "null" + }) } - dataList.push({ - param: key, - Value: value != null ? value : "null" - }) }) setData(dataList) } diff --git a/renderer/components/mainPages/application.jsx b/renderer/components/mainPages/application.jsx index 46f881db..55d746f0 100644 --- a/renderer/components/mainPages/application.jsx +++ b/renderer/components/mainPages/application.jsx @@ -358,9 +358,8 @@ const ApplicationPage = ({ pageId }) => { * @param {Array} columnsArray An array of the columns of the dataset */ const checkWarnings = (columnsArray) => { - let datasetColsString = JSON.stringify(columnsArray) - let modelColsString = JSON.stringify(modelFeatures) - if (datasetColsString !== modelColsString && modelFeatures && columnsArray) { + if (!modelFeatures.every((col) => columnsArray.includes(col)) && modelFeatures && columnsArray) { + const missingCols = modelFeatures.filter(col => !columnsArray.includes(col)) setDatasetHasWarning({ state: true, tooltip: ( @@ -371,17 +370,9 @@ const ApplicationPage = ({ pageId }) => {
Needed columns:
+Missing columns:
Received columns:
-