From b7f4651e2c09033c3f7235e509597cacd9d7f948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Montan=CC=83ana?= Date: Mon, 4 Aug 2025 13:10:48 +0200 Subject: [PATCH] Fix result name overlapping in simultaneous experiments --- CMakeLists.txt | 2 +- src/best/BestResults.cpp | 3 +-- src/manage/ResultsManager.cpp | 3 +-- src/results/Result.cpp | 46 +++++++++++++++++++++++++++++----- src/results/Result.h | 6 ++++- src/results/ResultsDataset.cpp | 3 +-- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08d87d7..1dfa19a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) project(Platform - VERSION 1.1.0 + VERSION 1.1.1 DESCRIPTION "Platform to run Experiments with classifiers." HOMEPAGE_URL "https://github.com/rmontanana/platform" LANGUAGES CXX diff --git a/src/best/BestResults.cpp b/src/best/BestResults.cpp index bd7f82a..053f535 100644 --- a/src/best/BestResults.cpp +++ b/src/best/BestResults.cpp @@ -41,8 +41,7 @@ namespace platform { } json bests; for (const auto& file : files) { - auto result = Result(); - result.load(path, file); + auto result = Result(path, file); auto data = result.getJson(); for (auto const& item : data.at("results")) { bool update = true; diff --git a/src/manage/ResultsManager.cpp b/src/manage/ResultsManager.cpp index fa4e895..dadd4ea 100644 --- a/src/manage/ResultsManager.cpp +++ b/src/manage/ResultsManager.cpp @@ -13,8 +13,7 @@ namespace platform { for (const auto& file : directory_iterator(path)) { auto filename = file.path().filename().string(); if (filename.find(".json") != std::string::npos && filename.find("results_") == 0) { - auto result = Result(); - result.load(path, filename); + auto result = Result(path, filename); bool addResult = true; if (platform != "any" && result.getPlatform() != platform || model != "any" && result.getModel() != model diff --git a/src/results/Result.cpp b/src/results/Result.cpp index f37ca61..6077e7c 100644 --- a/src/results/Result.cpp +++ b/src/results/Result.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include "best/BestScore.h" #include "common/Colors.h" #include "common/DotEnv.h" @@ -34,14 +36,25 @@ namespace platform { } Result::Result() { + path = Paths::results(); + fileName = "none"; data["date"] = get_actual_date(); data["time"] = get_actual_time(); data["results"] = json::array(); data["seeds"] = json::array(); + complete = false; } - - Result& Result::load(const std::string& path, const std::string& fileName) + std::string Result::getFilename() const { + if (fileName == "none") { + throw std::runtime_error("Filename is not set. Use save() method to generate a filename."); + } + return fileName; + } + Result::Result(const std::string& path, const std::string& fileName) + { + this->path = path; + this->fileName = fileName; std::ifstream resultData(path + "/" + fileName); if (resultData.is_open()) { data = json::parse(resultData); @@ -58,7 +71,6 @@ namespace platform { score /= best.second; } complete = data["results"].size() > 1; - return *this; } json Result::getJson() { @@ -71,11 +83,15 @@ namespace platform { } void Result::save(const std::string& path) { - std::ofstream file(path + getFilename()); + do { + fileName = generateFileName(); + } + while (std::filesystem::exists(path + fileName)); + std::ofstream file(path + fileName); file << data; file.close(); } - std::string Result::getFilename() const + std::string Result::generateFileName() { std::ostringstream oss; std::string stratified; @@ -85,13 +101,31 @@ namespace platform { catch (nlohmann::json_abi_v3_11_3::detail::type_error) { stratified = data["stratified"].get() == 1 ? "1" : "0"; } + auto generateRandomString = [](int length) -> std::string { + const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + // Use thread-local static generator to avoid interfering with global random state + thread_local static std::random_device rd; + thread_local static std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, sizeof(alphanum) - 2); + + std::string result; + for (int i = 0; i < length; ++i) { + result += alphanum[dis(gen)]; + } + return result; + }; oss << "results_" << data.at("score_name").get() << "_" << data.at("model").get() << "_" << data.at("platform").get() << "_" << data["date"].get() << "_" << data["time"].get() << "_" - << stratified << ".json"; + << stratified << "_" + << generateRandomString(5) << ".json"; return oss.str(); } std::string Result::to_string(int maxModel, int maxTitle) const diff --git a/src/results/Result.h b/src/results/Result.h index a16748d..5cbfea3 100644 --- a/src/results/Result.h +++ b/src/results/Result.h @@ -5,6 +5,7 @@ #include #include #include "common/Timer.hpp" +#include "common/Paths.h" #include "main/HyperParameters.h" #include "main/PartialResult.h" @@ -14,7 +15,7 @@ namespace platform { class Result { public: Result(); - Result& load(const std::string& path, const std::string& filename); + Result(const std::string& path, const std::string& filename); void save(const std::string& path); std::vector check(); // Getters @@ -49,6 +50,9 @@ namespace platform { void setNFolds(int nfolds) { data["folds"] = nfolds; }; void setPlatform(const std::string& platform_name) { data["platform"] = platform_name; }; private: + std::string generateFileName(); + std::string path; + std::string fileName; json data; bool complete; double score = 0.0; diff --git a/src/results/ResultsDataset.cpp b/src/results/ResultsDataset.cpp index 0ba8a1c..9377844 100644 --- a/src/results/ResultsDataset.cpp +++ b/src/results/ResultsDataset.cpp @@ -13,8 +13,7 @@ namespace platform { for (const auto& file : directory_iterator(path)) { auto filename = file.path().filename().string(); if (filename.find(".json") != std::string::npos && filename.find("results_") == 0) { - auto result = Result(); - result.load(path, filename); + auto result = Result(path, filename); if (model != "any" && result.getModel() != model) continue; auto data = result.getData()["results"];