From b9af086c294226aa9d76c2a2e3c976d258e4888a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana=20G=C3=B3mez?= Date: Sat, 16 Mar 2024 12:02:24 +0100 Subject: [PATCH] Refactor library folders Add paginators per output type in b_manage --- src/CMakeLists.txt | 34 +-- src/best/BestResults.cpp | 4 +- src/best/BestResultsExcel.cpp | 2 +- src/{best => commands}/b_best.cpp | 2 +- src/{grid => commands}/b_grid.cpp | 2 +- src/commands/b_list.cpp | 119 ++++++++++ src/{main => commands}/b_main.cpp | 6 +- src/{manage => commands}/b_manage.cpp | 2 +- src/list/b_list.cpp | 224 ------------------ src/main/Experiment.h | 2 +- src/manage/ManageResults.cpp | 190 ++++++++++----- src/manage/ManageResults.h | 21 +- src/manage/Paginator.hpp | 28 ++- src/manage/ResultsManager.h | 2 +- src/reports/DatasetsConsole.hpp | 78 ++++++ src/{list => reports}/DatasetsExcel.cpp | 0 src/{list => reports}/DatasetsExcel.h | 0 src/{main => results}/Result.cpp | 0 src/{main => results}/Result.h | 0 src/{list => results}/ResultsDataset.cpp | 0 src/{list => results}/ResultsDataset.h | 2 +- src/results/ResultsDatasetConsole.hpp | 95 ++++++++ src/{list => results}/ResultsDatasetExcel.cpp | 0 src/{list => results}/ResultsDatasetExcel.h | 0 24 files changed, 488 insertions(+), 325 deletions(-) rename src/{best => commands}/b_best.cpp (99%) rename src/{grid => commands}/b_grid.cpp (99%) create mode 100644 src/commands/b_list.cpp rename src/{main => commands}/b_main.cpp (98%) rename src/{manage => commands}/b_manage.cpp (98%) delete mode 100644 src/list/b_list.cpp create mode 100644 src/reports/DatasetsConsole.hpp rename src/{list => reports}/DatasetsExcel.cpp (100%) rename src/{list => reports}/DatasetsExcel.h (100%) rename src/{main => results}/Result.cpp (100%) rename src/{main => results}/Result.h (100%) rename src/{list => results}/ResultsDataset.cpp (100%) rename src/{list => results}/ResultsDataset.h (97%) create mode 100644 src/results/ResultsDatasetConsole.hpp rename src/{list => results}/ResultsDatasetExcel.cpp (100%) rename src/{list => results}/ResultsDatasetExcel.h (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 957316b..1d39579 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,55 +13,55 @@ include_directories( /usr/local/include ## Platform ${Platform_SOURCE_DIR}/src + ${Platform_SOURCE_DIR}/results ) # b_best -set(best_sources b_best.cpp BestResults.cpp Statistics.cpp BestResultsExcel.cpp) -list(TRANSFORM best_sources PREPEND best/) add_executable( - b_best ${best_sources} + b_best commands/b_best.cpp best/Statistics.cpp + best/BestResultsExcel.cpp best/BestResults.cpp common/Datasets.cpp common/Dataset.cpp - main/Result.cpp main/Models.cpp - reports/ReportExcel.cpp reports/ReportBase.cpp reports/ExcelFile.cpp + main/Models.cpp + reports/ReportExcel.cpp reports/ReportBase.cpp reports/ExcelFile.cpp + results/Result.cpp ) target_link_libraries(b_best Boost::boost "${PyClassifiers}" "${BayesNet}" ArffFiles mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}") # b_grid -set(grid_sources b_grid.cpp GridSearch.cpp GridData.cpp) +set(grid_sources GridSearch.cpp GridData.cpp) list(TRANSFORM grid_sources PREPEND grid/) -add_executable(b_grid ${grid_sources} +add_executable(b_grid commands/b_grid.cpp ${grid_sources} common/Datasets.cpp common/Dataset.cpp main/HyperParameters.cpp main/Models.cpp ) target_link_libraries(b_grid ${MPI_CXX_LIBRARIES} "${PyClassifiers}" "${BayesNet}" ArffFiles mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy) # b_list -set(list_sources b_list.cpp DatasetsExcel.cpp ResultsDataset.cpp ResultsDatasetExcel.cpp) -list(TRANSFORM list_sources PREPEND list/) -add_executable(b_list ${list_sources} +add_executable(b_list commands/b_list.cpp ${list_sources} common/Datasets.cpp common/Dataset.cpp main/Models.cpp - reports/ReportExcel.cpp reports/ExcelFile.cpp reports/ReportBase.cpp main/Result.cpp + reports/ReportExcel.cpp reports/ExcelFile.cpp reports/ReportBase.cpp reports/DatasetsExcel.cpp + results/Result.cpp results/ResultsDatasetExcel.cpp results/ResultsDataset.cpp ) target_link_libraries(b_list "${PyClassifiers}" "${BayesNet}" ArffFiles mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}") # b_main -set(main_sources b_main.cpp Experiment.cpp Models.cpp HyperParameters.cpp) +set(main_sources Experiment.cpp Models.cpp HyperParameters.cpp) list(TRANSFORM main_sources PREPEND main/) -add_executable(b_main ${main_sources} +add_executable(b_main commands/b_main.cpp ${main_sources} common/Datasets.cpp common/Dataset.cpp - main/Result.cpp reports/ReportConsole.cpp reports/ReportBase.cpp + results/Result.cpp ) target_link_libraries(b_main "${PyClassifiers}" "${BayesNet}" ArffFiles mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy) # b_manage -set(manage_sources b_manage.cpp ManageResults.cpp CommandParser.cpp ResultsManager.cpp) +set(manage_sources ManageResults.cpp CommandParser.cpp ResultsManager.cpp) list(TRANSFORM manage_sources PREPEND manage/) add_executable( - b_manage ${manage_sources} + b_manage commands/b_manage.cpp ${manage_sources} common/Datasets.cpp common/Dataset.cpp - main/Result.cpp reports/ReportConsole.cpp reports/ReportExcel.cpp reports/ReportExcelCompared.cpp reports/ReportBase.cpp reports/ExcelFile.cpp + results/Result.cpp ) target_link_libraries(b_manage "${TORCH_LIBRARIES}" "${XLSXWRITER_LIB}" ArffFiles mdlp) diff --git a/src/best/BestResults.cpp b/src/best/BestResults.cpp index 1bc05c4..1c97bd3 100644 --- a/src/best/BestResults.cpp +++ b/src/best/BestResults.cpp @@ -6,9 +6,9 @@ #include #include "common/Colors.h" #include "common/CLocale.h" -#include "main/Result.h" +#include "results/Result.h" #include "BestResultsExcel.h" -#include "Statistics.h" +#include "best/Statistics.h" #include "BestResults.h" diff --git a/src/best/BestResultsExcel.cpp b/src/best/BestResultsExcel.cpp index 4250ec7..a456721 100644 --- a/src/best/BestResultsExcel.cpp +++ b/src/best/BestResultsExcel.cpp @@ -3,7 +3,7 @@ #include #include "common/Paths.h" #include "reports/ReportExcel.h" -#include "Statistics.h" +#include "best/Statistics.h" #include "BestResultsExcel.h" namespace platform { diff --git a/src/best/b_best.cpp b/src/commands/b_best.cpp similarity index 99% rename from src/best/b_best.cpp rename to src/commands/b_best.cpp index 68576f8..f074e9c 100644 --- a/src/best/b_best.cpp +++ b/src/commands/b_best.cpp @@ -4,7 +4,7 @@ #include "main/modelRegister.h" #include "common/Paths.h" #include "common/Colors.h" -#include "BestResults.h" +#include "best/BestResults.h" #include "config.h" void manageArguments(argparse::ArgumentParser& program) diff --git a/src/grid/b_grid.cpp b/src/commands/b_grid.cpp similarity index 99% rename from src/grid/b_grid.cpp rename to src/commands/b_grid.cpp index b478c64..f9fc56d 100644 --- a/src/grid/b_grid.cpp +++ b/src/commands/b_grid.cpp @@ -10,7 +10,7 @@ #include "common/Timer.h" #include "common/Colors.h" #include "common/DotEnv.h" -#include "GridSearch.h" +#include "grid/GridSearch.h" #include "config.h" using json = nlohmann::json; diff --git a/src/commands/b_list.cpp b/src/commands/b_list.cpp new file mode 100644 index 0000000..5c49ce8 --- /dev/null +++ b/src/commands/b_list.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include "main/Models.h" +#include "main/modelRegister.h" +#include "common/Paths.h" +#include "common/Colors.h" +#include "common/Datasets.h" +#include "reports/DatasetsExcel.h" +#include "reports/DatasetsConsole.hpp" +#include "results/ResultsDataset.h" +#include "results/ResultsDatasetExcel.h" +#include "results/ResultsDatasetConsole.hpp" +#include "config.h" + + +void list_datasets(argparse::ArgumentParser& program) +{ + auto excel = program.get("excel"); + auto report = platform::DatasetsConsole(); + report.list_datasets(); + std::cout << report.getOutput(); + if (excel) { + auto data = report.getData(); + auto report = platform::DatasetsExcel(); + report.report(data); + std::cout << std::endl << Colors::GREEN() << "Output saved in " << report.getFileName() << std::endl; + } +} + +void list_results(argparse::ArgumentParser& program) +{ + auto dataset = program.get("dataset"); + auto score = program.get("score"); + auto model = program.get("model"); + auto excel = program.get("excel"); + auto report = platform::ResultsDatasetsConsole(); + report.list_results(dataset, score, model); + std::cout << report.getOutput(); + if (excel) { + auto data = report.getData(); + auto report = platform::ResultsDatasetExcel(); + report.report(data); + std::cout << std::endl << Colors::GREEN() << "Output saved in " << report.getFileName() << std::endl; + } +} + +int main(int argc, char** argv) +{ + argparse::ArgumentParser program("b_list", { platform_project_version.begin(), platform_project_version.end() }); + // + // datasets subparser + // + argparse::ArgumentParser datasets_command("datasets"); + datasets_command.add_description("List datasets available in the platform."); + datasets_command.add_argument("--excel").help("Output in Excel format").default_value(false).implicit_value(true); + // + // results subparser + // + argparse::ArgumentParser results_command("results"); + results_command.add_description("List the results of a given dataset."); + auto datasets = platform::Datasets(false, platform::Paths::datasets()); + results_command.add_argument("-d", "--dataset") + .help("Dataset to use " + datasets.toString()) + .required() + .action([](const std::string& value) { + auto datasets = platform::Datasets(false, platform::Paths::datasets()); + static const std::vector choices = datasets.getNames(); + if (find(choices.begin(), choices.end(), value) != choices.end()) { + return value; + } + throw std::runtime_error("Dataset must be one of " + datasets.toString()); + } + ); + results_command.add_argument("-m", "--model") + .help("Model to use: " + platform::Models::instance()->toString() + " or any") + .default_value("any") + .action([](const std::string& value) { + std::vector valid(platform::Models::instance()->getNames()); + valid.push_back("any"); + static const std::vector choices = valid; + if (find(choices.begin(), choices.end(), value) != choices.end()) { + return value; + } + throw std::runtime_error("Model must be one of " + platform::Models::instance()->toString() + " or any"); + } + ); + results_command.add_argument("--excel").help("Output in Excel format").default_value(false).implicit_value(true); + results_command.add_argument("-s", "--score").default_value("accuracy").help("Filter results of the score name supplied"); + + // Add subparsers + program.add_subparser(datasets_command); + program.add_subparser(results_command); + // Parse command line and execute + try { + program.parse_args(argc, argv); + bool found = false; + map commands = { {"datasets", &list_datasets}, {"results", &list_results} }; + for (const auto& command : commands) { + if (program.is_subcommand_used(command.first)) { + std::invoke(command.second, program.at(command.first)); + found = true; + break; + } + } + if (!found) { + throw std::runtime_error("You must specify one of the following commands: datasets, results\n"); + } + } + catch (const exception& err) { + cerr << err.what() << std::endl; + cerr << program; + exit(1); + } + std::cout << Colors::RESET() << std::endl; + return 0; +} \ No newline at end of file diff --git a/src/main/b_main.cpp b/src/commands/b_main.cpp similarity index 98% rename from src/main/b_main.cpp rename to src/commands/b_main.cpp index 33a6f25..bab21e3 100644 --- a/src/main/b_main.cpp +++ b/src/commands/b_main.cpp @@ -1,12 +1,12 @@ #include #include #include -#include "Experiment.h" +#include "main/Experiment.h" #include "common/Datasets.h" #include "common/DotEnv.h" #include "common/Paths.h" -#include "Models.h" -#include "modelRegister.h" +#include "main/Models.h" +#include "main/modelRegister.h" #include "config.h" diff --git a/src/manage/b_manage.cpp b/src/commands/b_manage.cpp similarity index 98% rename from src/manage/b_manage.cpp rename to src/commands/b_manage.cpp index ce32c43..83e7409 100644 --- a/src/manage/b_manage.cpp +++ b/src/commands/b_manage.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "ManageResults.h" +#include "manage/ManageResults.h" #include "config.h" void manageArguments(argparse::ArgumentParser& program, int argc, char** argv) diff --git a/src/list/b_list.cpp b/src/list/b_list.cpp deleted file mode 100644 index f0f74d6..0000000 --- a/src/list/b_list.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include -#include -#include -#include "main/Models.h" -#include "main/modelRegister.h" -#include "common/Paths.h" -#include "common/Colors.h" -#include "common/Datasets.h" -#include "DatasetsExcel.h" -#include "ResultsDataset.h" -#include "ResultsDatasetExcel.h" -#include "config.h" - -const int BALANCE_LENGTH = 75; - -struct separated : numpunct { - char do_decimal_point() const { return ','; } - char do_thousands_sep() const { return '.'; } - std::string do_grouping() const { return "\03"; } -}; - -std::string outputBalance(const std::string& balance) -{ - auto temp = std::string(balance); - while (temp.size() > BALANCE_LENGTH - 1) { - auto part = temp.substr(0, BALANCE_LENGTH); - std::cout << part << std::endl; - std::cout << setw(52) << " "; - temp = temp.substr(BALANCE_LENGTH); - } - return temp; -} - -void list_datasets(argparse::ArgumentParser& program) -{ - auto datasets = platform::Datasets(false, platform::Paths::datasets()); - auto excel = program.get("excel"); - locale mylocale(std::cout.getloc(), new separated); - locale::global(mylocale); - std::cout.imbue(mylocale); - std::cout << Colors::GREEN() << " # Dataset Sampl. Feat. Cls Balance" << std::endl; - std::string balanceBars = std::string(BALANCE_LENGTH, '='); - std::cout << "=== ============================== ====== ===== === " << balanceBars << std::endl; - int num = 0; - json data; - for (const auto& dataset : datasets.getNames()) { - auto color = num % 2 ? Colors::CYAN() : Colors::BLUE(); - std::cout << color << setw(3) << right << num++ << " "; - std::cout << setw(30) << left << dataset << " "; - datasets.loadDataset(dataset); - auto nSamples = datasets.getNSamples(dataset); - std::cout << setw(6) << right << nSamples << " "; - std::cout << setw(5) << right << datasets.getFeatures(dataset).size() << " "; - std::cout << setw(3) << right << datasets.getNClasses(dataset) << " "; - std::stringstream oss; - std::string sep = ""; - for (auto number : datasets.getClassesCounts(dataset)) { - oss << sep << std::setprecision(2) << fixed << (float)number / nSamples * 100.0 << "% (" << number << ")"; - sep = " / "; - } - auto balance = outputBalance(oss.str()); - std::cout << balance << std::endl; - // Store data for Excel report - data[dataset] = json::object(); - data[dataset]["samples"] = nSamples; - data[dataset]["features"] = datasets.getFeatures(dataset).size(); - data[dataset]["classes"] = datasets.getNClasses(dataset); - data[dataset]["balance"] = oss.str(); - } - if (excel) { - auto report = platform::DatasetsExcel(); - report.report(data); - std::cout << std::endl << Colors::GREEN() << "Output saved in " << report.getFileName() << std::endl; - } -} - -void list_results(argparse::ArgumentParser& program) -{ - auto dataset = program.get("dataset"); - auto score = program.get("score"); - auto model = program.get("model"); - auto excel = program.get("excel"); - auto results = platform::ResultsDataset(dataset, model, score); - results.load(); - results.sortModel(); - if (results.empty()) { - std::cerr << Colors::RED() << "No results found for dataset " << dataset << " and model " << model << Colors::RESET() << std::endl; - exit(1); - } - int maxModel = results.maxModelSize(); - int maxHyper = results.maxHyperSize(); - double maxResult = results.maxResultScore(); - // Build data for the Report - json data = json::object(); - data["results"] = json::array(); - data["max_models"] = json::object(); // Max score per model - for (const auto& result : results) { - auto results = result.getData(); - if (!data["max_models"].contains(result.getModel())) { - data["max_models"][result.getModel()] = 0; - } - for (const auto& item : results["results"]) { - if (item["dataset"] == dataset) { - - // Store data for Excel report - json res = json::object(); - res["date"] = result.getDate(); - res["time"] = result.getTime(); - res["model"] = result.getModel(); - res["score"] = item["score"].get(); - res["hyperparameters"] = item["hyperparameters"].dump(); - data["results"].push_back(res); - if (item["score"].get() > data["max_models"][result.getModel()]) { - data["max_models"][result.getModel()] = item["score"].get(); - } - break; - } - } - } - // - // List the results - // - std::cout << Colors::GREEN() << "Results of dataset " << dataset << " - for " << model << " model" << std::endl; - std::cout << "There are " << results.size() << " results" << std::endl; - std::cout << Colors::GREEN() << " # " << std::setw(maxModel + 1) << std::left << "Model" << "Date Time Score Hyperparameters" << std::endl; - std::cout << "=== " << std::string(maxModel, '=') << " ========== ======== =========== " << std::string(maxHyper, '=') << std::endl; - auto i = 0; - for (const auto& item : data["results"]) { - auto color = (i % 2) ? Colors::BLUE() : Colors::CYAN(); - auto score = item["score"].get(); - color = score == data["max_models"][item["model"].get()] ? Colors::YELLOW() : color; - color = score == maxResult ? Colors::RED() : color; - std::cout << color << std::setw(3) << std::fixed << std::right << i++ << " "; - std::cout << std::setw(maxModel) << std::left << item["model"].get() << " "; - std::cout << color << item["date"].get() << " "; - std::cout << color << item["time"].get() << " "; - std::cout << std::setw(11) << std::setprecision(9) << std::fixed << score << " "; - std::cout << item["hyperparameters"].get() << std::endl; - } - if (excel) { - data["dataset"] = dataset; - data["score"] = score; - data["model"] = model; - data["lengths"]["maxModel"] = maxModel; - data["lengths"]["maxHyper"] = maxHyper; - data["maxResult"] = maxResult; - auto report = platform::ResultsDatasetExcel(); - report.report(data); - std::cout << std::endl << Colors::GREEN() << "Output saved in " << report.getFileName() << std::endl; - } -} - -int main(int argc, char** argv) -{ - argparse::ArgumentParser program("b_list", { platform_project_version.begin(), platform_project_version.end() }); - // - // datasets subparser - // - argparse::ArgumentParser datasets_command("datasets"); - datasets_command.add_description("List datasets available in the platform."); - datasets_command.add_argument("--excel").help("Output in Excel format").default_value(false).implicit_value(true); - // - // results subparser - // - argparse::ArgumentParser results_command("results"); - results_command.add_description("List the results of a given dataset."); - auto datasets = platform::Datasets(false, platform::Paths::datasets()); - results_command.add_argument("-d", "--dataset") - .help("Dataset to use " + datasets.toString()) - .required() - .action([](const std::string& value) { - auto datasets = platform::Datasets(false, platform::Paths::datasets()); - static const std::vector choices = datasets.getNames(); - if (find(choices.begin(), choices.end(), value) != choices.end()) { - return value; - } - throw std::runtime_error("Dataset must be one of " + datasets.toString()); - } - ); - results_command.add_argument("-m", "--model") - .help("Model to use: " + platform::Models::instance()->toString() + " or any") - .default_value("any") - .action([](const std::string& value) { - std::vector valid(platform::Models::instance()->getNames()); - valid.push_back("any"); - static const std::vector choices = valid; - if (find(choices.begin(), choices.end(), value) != choices.end()) { - return value; - } - throw std::runtime_error("Model must be one of " + platform::Models::instance()->toString() + " or any"); - } - ); - results_command.add_argument("--excel").help("Output in Excel format").default_value(false).implicit_value(true); - results_command.add_argument("-s", "--score").default_value("accuracy").help("Filter results of the score name supplied"); - - // Add subparsers - program.add_subparser(datasets_command); - program.add_subparser(results_command); - // Parse command line and execute - try { - program.parse_args(argc, argv); - bool found = false; - map commands = { {"datasets", &list_datasets}, {"results", &list_results} }; - for (const auto& command : commands) { - if (program.is_subcommand_used(command.first)) { - std::invoke(command.second, program.at(command.first)); - found = true; - break; - } - } - if (!found) { - throw std::runtime_error("You must specify one of the following commands: datasets, results\n"); - } - } - catch (const exception& err) { - cerr << err.what() << std::endl; - cerr << program; - exit(1); - } - std::cout << Colors::RESET() << std::endl; - return 0; -} \ No newline at end of file diff --git a/src/main/Experiment.h b/src/main/Experiment.h index 6e8c384..a24ef5d 100644 --- a/src/main/Experiment.h +++ b/src/main/Experiment.h @@ -6,7 +6,7 @@ #include #include "bayesnet/BaseClassifier.h" #include "HyperParameters.h" -#include "Result.h" +#include "results/Result.h" namespace platform { using json = nlohmann::json; diff --git a/src/manage/ManageResults.cpp b/src/manage/ManageResults.cpp index 97d6cf7..9aa4b06 100644 --- a/src/manage/ManageResults.cpp +++ b/src/manage/ManageResults.cpp @@ -3,11 +3,13 @@ #include "common/Colors.h" #include "common/CLocale.h" #include "common/Paths.h" +#include "CommandParser.h" +#include "ManageResults.h" +// #include "reports/DatasetsConsole.hpp" #include "reports/ReportConsole.h" #include "reports/ReportExcel.h" #include "reports/ReportExcelCompared.h" -#include "CommandParser.h" -#include "ManageResults.h" + namespace platform { const std::string STATUS_OK = "Ok."; @@ -24,8 +26,14 @@ namespace platform { if (numFiles == 0 or numFiles > results.size()) { this->numFiles = results.size(); } - paginator = Paginator(numFiles, results.size()); - page = 1; + // Initializes the paginator for each output type (experiments, datasets, result) + for (int i = 0; i < static_cast(OutputType::Count); i++) { + paginator.push_back(Paginator(numFiles, results.size())); + } + index_A = -1; + index_B = -1; + max_status_line = 140; + output_type = OutputType::EXPERIMENTS; } void ManageResults::doMenu() { @@ -34,7 +42,7 @@ namespace platform { return; } results.sortDate(); - list(STATUS_OK, STATUS_COLOR, -1, -1); + list(STATUS_OK, STATUS_COLOR); menu(); if (openExcel) { workbook_close(workbook); @@ -44,17 +52,9 @@ namespace platform { } std::cout << Colors::RESET() << "Done!" << std::endl; } - void ManageResults::list(const std::string& status_message_init, const std::string& status_color, int index_A, int index_B) + void ManageResults::header() { - // - // Page info - // - int maxModel = results.maxModelSize(); - int maxTitle = results.maxTitleSize(); - std::vector header_lengths = { 3, 10, maxModel, 10, 9, 3, 7, maxTitle }; - int maxLine = std::max(size_t(140), std::accumulate(header_lengths.begin(), header_lengths.end(), 0) + header_lengths.size() - 1); - auto temp = ConfigLocale(); - auto [index_from, index_to] = paginator.getOffset(page); + auto [index_from, index_to] = paginator[static_cast(output_type)].getOffset(); std::string suffix = ""; if (complete) { suffix = " Only listing complete results "; @@ -62,15 +62,86 @@ namespace platform { if (partial) { suffix = " Only listing partial results "; } - std::string header = " " + std::to_string(index_to - index_from + 1) + " Results on screen of " - + std::to_string(results.size()) + " - Page " + std::to_string(page) + " of " - + std::to_string(paginator.getPages()) + " "; + auto page = paginator[static_cast(output_type)].getPage(); + auto pages = paginator[static_cast(output_type)].getPages(); + auto lines = paginator[static_cast(output_type)].getLines(); + auto total = paginator[static_cast(output_type)].getTotal(); + std::string header = " Lines " + std::to_string(lines) + " " + + std::to_string(total) + " - Page " + std::to_string(page) + " of " + + std::to_string(pages) + " "; - std::string prefix = std::string(maxLine - suffix.size() - header.size(), ' '); + std::string prefix = std::string(max_status_line - suffix.size() - header.size(), ' '); std::cout << Colors::CLRSCR() << Colors::REVERSE() << Colors::WHITE() << header << prefix << Colors::MAGENTA() << suffix << std::endl; + } + void ManageResults::footer(const std::string& status, const std::string& status_color) + { + std::stringstream oss; + oss << " A: " << (index_A == -1 ? "" : std::to_string(index_A)) << + " B: " << (index_B == -1 ? "" : std::to_string(index_B)) << " "; + int status_length = std::max(oss.str().size(), max_status_line - oss.str().size()); + auto status_message = status.substr(0, status_length - 1); + std::string status_line = status_message + std::string(std::max(size_t(0), status_length - status_message.size() - 1), ' '); + auto color = (index_A != -1 && index_B != -1) ? Colors::IGREEN() : Colors::IYELLOW(); + std::cout << color << Colors::REVERSE() << oss.str() << Colors::RESET() << Colors::WHITE() + << Colors::REVERSE() << status_color << " " << status_line << Colors::IWHITE() + << Colors::RESET() << std::endl; + } + void ManageResults::list(const std::string& status_message, const std::string& status_color) + { + switch (output_type) { + case OutputType::RESULT: + list_result(status_message, status_color); + break; + case OutputType::DATASETS: + list_datasets(status_message, status_color); + break; + case OutputType::EXPERIMENTS: + list_experiments(status_message, status_color); + break; + } + } + void ManageResults::list_result(const std::string& status_message, const std::string& status_color) + { + // + // header + // + header(); + // + // Status Area + // + footer(status_message, status_color); + + } + void ManageResults::list_datasets(const std::string& status_message, const std::string& status_color) + { + // auto report = DatasetsConsole(); + // report.list_datasets(); + // auto output = report.getOutput(); + // paginator[static_cast(output_type)].setTotal(report.getNumLines()); + // + // header + // + header(); + + // + // Status Area + // + footer(status_message, status_color); + + } + void ManageResults::list_experiments(const std::string& status_message, const std::string& status_color) + { + // + // header + // + header(); // // Field names // + int maxModel = results.maxModelSize(); + int maxTitle = results.maxTitleSize(); + std::vector header_lengths = { 3, 10, maxModel, 10, 9, 3, 7, maxTitle }; + // std::cout << Colors::RESET(); std::string arrow = Symbols::downward_arrow + " "; std::vector header_labels = { " #", "Date", "Model", "Score Name", "Score", "C/P", "Time", "Title" }; @@ -92,6 +163,7 @@ namespace platform { // // Results // + auto [index_from, index_to] = paginator[static_cast(output_type)].getOffset(); for (int i = index_from; i <= index_to; i++) { auto color = (i % 2) ? Colors::BLUE() : Colors::CYAN(); std::cout << color << std::setw(3) << std::fixed << std::right << i << " "; @@ -100,16 +172,7 @@ namespace platform { // // Status Area // - std::stringstream oss; - oss << " A: " << (index_A == -1 ? "" : std::to_string(index_A)) << - " B: " << (index_B == -1 ? "" : std::to_string(index_B)) << " "; - int status_length = std::max(oss.str().size(), maxLine - oss.str().size()); - auto status_message = status_message_init.substr(0, status_length - 1); - std::string status = status_message + std::string(std::max(size_t(0), status_length - status_message.size()), ' '); - auto color = (index_A != -1 && index_B != -1) ? Colors::IGREEN() : Colors::IYELLOW(); - std::cout << color << Colors::REVERSE() << oss.str() << Colors::RESET() << Colors::WHITE() - << Colors::REVERSE() << status_color << " " << status << Colors::IWHITE() - << Colors::RESET() << std::endl; + footer(status_message, status_color); } bool ManageResults::confirmAction(const std::string& intent, const std::string& fileName) const { @@ -132,7 +195,7 @@ namespace platform { std::cout << "Not done!" << std::endl; return false; } - std::string ManageResults::report_compared(const int index_A, const int index_B) + std::string ManageResults::report_compared() { auto data_A = results.at(index_A).getJson(); auto data_B = results.at(index_B).getJson(); @@ -198,14 +261,15 @@ namespace platform { void ManageResults::menu() { char option; - int index, subIndex, index_A = -1, index_B = -1; + int index, subIndex; bool finished = false; std::string filename; // tuple std::vector> mainOptions = { {"quit", 'q', false}, {"list", 'l', false}, - {"delete", 'd', true}, + {"delete", 'x', true}, + {"datasets", 'd', false}, {"hide", 'h', true}, {"sort", 's', false}, {"report", 'r', true}, @@ -230,41 +294,42 @@ namespace platform { bool parserError = true; // force the first iteration while (parserError) { if (indexList) { - auto [min_index, max_index] = paginator.getOffset(page); + auto [min_index, max_index] = paginator[static_cast(output_type)].getOffset(); std::tie(option, index, parserError) = parser.parse(Colors::IGREEN(), mainOptions, 'r', min_index, max_index); } else { std::tie(option, subIndex, parserError) = parser.parse(Colors::IBLUE(), listOptions, 'r', 0, results.at(index).getJson()["results"].size() - 1); } if (parserError) { if (indexList) - list(parser.getErrorMessage(), Colors::RED(), index_A, index_B); + list(parser.getErrorMessage(), Colors::RED()); else report(index, false); } } switch (option) { + case 'd': + output_type = OutputType::DATASETS; + list_datasets(STATUS_OK, STATUS_COLOR); + break; case 'p': - if (paginator.valid(index)) { - page = index; - list(STATUS_OK, STATUS_COLOR, index_A, index_B); + if (paginator[static_cast(output_type)].setPage(index)) { + list(STATUS_OK, STATUS_COLOR); } else { - list("Invalid page!", Colors::RED(), index_A, index_B); + list("Invalid page!", Colors::RED()); } break; case '+': - if (paginator.hasNext(page)) { - page++; - list(STATUS_OK, STATUS_COLOR, index_A, index_B); + if (paginator[static_cast(output_type)].addPage()) { + list(STATUS_OK, STATUS_COLOR); } else { - list("No more pages!", Colors::RED(), index_A, index_B); + list("No more pages!", Colors::RED()); } break; case '-': - if (paginator.hasPrev(page)) { - page--; - list(STATUS_OK, STATUS_COLOR, index_A, index_B); + if (paginator[static_cast(output_type)].subPage()) { + list(STATUS_OK, STATUS_COLOR); } else { - list("First page already!", Colors::RED(), index_A, index_B); + list("First page already!", Colors::RED()); } break; case 'q': @@ -272,20 +337,20 @@ namespace platform { break; case 'a': if (index == index_B) { - list("A and B cannot be the same!", Colors::RED(), index_A, index_B); + list("A and B cannot be the same!", Colors::RED()); break; } index_A = index; - list("A set to " + std::to_string(index), Colors::GREEN(), index_A, index_B); + list("A set to " + std::to_string(index), Colors::GREEN()); break; case 'b': if (indexList) { if (index == index_A) { - list("A and B cannot be the same!", Colors::RED(), index_A, index_B); + list("A and B cannot be the same!", Colors::RED()); break; } index_B = index; - list("B set to " + std::to_string(index), Colors::GREEN(), index_A, index_B); + list("B set to " + std::to_string(index), Colors::GREEN()); } else { // back to show the report report(index, false); @@ -293,45 +358,46 @@ namespace platform { break; case 'c': if (index_A == -1 || index_B == -1) { - list("Need to set A and B first!", Colors::RED(), index_A, index_B); + list("Need to set A and B first!", Colors::RED()); break; } - list(report_compared(index_A, index_B), Colors::GREEN(), index_A, index_B); + list(report_compared(), Colors::GREEN()); break; case 'l': - list(STATUS_OK, STATUS_COLOR, index_A, index_B); + output_type = OutputType::EXPERIMENTS; + list(STATUS_OK, STATUS_COLOR); indexList = true; break; - case 'd': + case 'x': filename = results.at(index).getFilename(); if (!confirmAction("delete", filename)) { - list(filename + " not deleted!", Colors::YELLOW(), index_A, index_B); + list(filename + " not deleted!", Colors::YELLOW()); break; } std::cout << "Deleting " << filename << std::endl; results.deleteResult(index); - list(filename + " deleted!", Colors::RED(), index_A, index_B); + list(filename + " deleted!", Colors::RED()); break; case 'h': { std::string status_message; filename = results.at(index).getFilename(); if (!confirmAction("hide", filename)) { - list(filename + " not hidden!", Colors::YELLOW(), index_A, index_B); + list(filename + " not hidden!", Colors::YELLOW()); break; } filename = results.at(index).getFilename(); std::cout << "Hiding " << filename << std::endl; results.hideResult(index, Paths::hiddenResults()); status_message = filename + " hidden! (moved to " + Paths::hiddenResults() + ")"; - list(status_message, Colors::YELLOW(), index_A, index_B); + list(status_message, Colors::YELLOW()); } break; case 's': { std::string status_message, status_color; tie(status_color, status_message) = sortList(); - list(status_message, status_color, index_A, index_B); + list(status_message, status_color); } break; case 'r': @@ -343,7 +409,7 @@ namespace platform { } break; case 'e': - list(report(index, true), Colors::GREEN(), index_A, index_B); + list(report(index, true), Colors::GREEN()); break; case 't': { @@ -356,10 +422,10 @@ namespace platform { results.at(index).setTitle(newTitle); results.at(index).save(); status_message = "Title changed to " + newTitle; - list(status_message, Colors::GREEN(), index_A, index_B); + list(status_message, Colors::GREEN()); break; } - list("No title change!", Colors::YELLOW(), index_A, index_B); + list("No title change!", Colors::YELLOW()); } break; } diff --git a/src/manage/ManageResults.h b/src/manage/ManageResults.h index 0bdceb0..8fa163d 100644 --- a/src/manage/ManageResults.h +++ b/src/manage/ManageResults.h @@ -5,20 +5,34 @@ #include "Paginator.hpp" namespace platform { + enum class OutputType { + EXPERIMENTS = 0, + DATASETS = 1, + RESULT = 2, + Count + }; class ManageResults { public: ManageResults(int numFiles, const std::string& model, const std::string& score, bool complete, bool partial, bool compare); ~ManageResults() = default; void doMenu(); private: - void list(const std::string& status, const std::string& color, int index_A, int index_B); + void list(const std::string& status, const std::string& color); + void list_experiments(const std::string& status, const std::string& color); + void list_result(const std::string& status, const std::string& color); + void list_datasets(const std::string& status, const std::string& color); bool confirmAction(const std::string& intent, const std::string& fileName) const; std::string report(const int index, const bool excelReport); - std::string report_compared(const int index_A, const int index_B); + std::string report_compared(); void showIndex(const int index, const int idx); std::pair sortList(); void menu(); + void header(); + void footer(const std::string& status, const std::string& color); + OutputType output_type; int numFiles; + int index_A, index_B; // used for comparison of experiments + int max_status_line; bool indexList; bool openExcel; bool didExcel; @@ -26,8 +40,7 @@ namespace platform { bool partial; bool compare; std::string sort_field; - int page; - Paginator paginator; + std::vector paginator; ResultsManager results; lxw_workbook* workbook; }; diff --git a/src/manage/Paginator.hpp b/src/manage/Paginator.hpp index ba3570d..bb384a2 100644 --- a/src/manage/Paginator.hpp +++ b/src/manage/Paginator.hpp @@ -5,25 +5,41 @@ class Paginator { public: Paginator() = default; - Paginator(int pageSize, int total) : pageSize(pageSize), total(total) + Paginator(int pageSize, int total, int page = 1) : pageSize(pageSize), total(total), page(page) { - numPages = pageSize > 0 ? (total + pageSize - 1) / pageSize : 0; + computePages(); }; ~Paginator() = default; int getPageSize() const { return pageSize; } - int getTotal() const { return total; } - std::pair getOffset(int page) const + int getLines() const + { + auto [start, end] = getOffset(); + return std::min(pageSize, end - start + 1); + } + int getTotal() const { return total; } + void setTotal(int total) { this->total = total; computePages(); } + std::pair getOffset() const { - if (page > numPages) - throw std::out_of_range("page out of range"); return { (page - 1) * pageSize, std::min(total - 1, page * pageSize - 1) }; } int getPages() const { return numPages; } + int getPage() const { return page; } bool valid(int page) const { return page > 0 && page <= numPages; } bool hasPrev(int page) const { return page > 1; } bool hasNext(int page) const { return page < getPages(); } + bool setPage(int page) { return valid(page) ? this->page = page, true : false; } + bool addPage() { return page < numPages ? ++page, true : false; } + bool subPage() { return page > 1 ? --page, true : false; } private: + void computePages() + { + numPages = pageSize > 0 ? (total + pageSize - 1) / pageSize : 0; + if (page > numPages) { + page = numPages; + } + } int pageSize; int total; + int page; int numPages; }; \ No newline at end of file diff --git a/src/manage/ResultsManager.h b/src/manage/ResultsManager.h index c52211a..308259d 100644 --- a/src/manage/ResultsManager.h +++ b/src/manage/ResultsManager.h @@ -3,7 +3,7 @@ #include #include #include -#include "main/Result.h" +#include "results/Result.h" namespace platform { using json = nlohmann::json; class ResultsManager { diff --git a/src/reports/DatasetsConsole.hpp b/src/reports/DatasetsConsole.hpp new file mode 100644 index 0000000..c1ec0e8 --- /dev/null +++ b/src/reports/DatasetsConsole.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include "common/Colors.h" +#include "common/Datasets.h" + +namespace platform { + const int BALANCE_LENGTH = 75; + struct separated_datasets : numpunct { + char do_decimal_point() const { return ','; } + char do_thousands_sep() const { return '.'; } + std::string do_grouping() const { return "\03"; } + }; + + class DatasetsConsole { + public: + DatasetsConsole() = default; + ~DatasetsConsole() = default; + std::string getOutput() const { return output.str(); } + int getNumLines() const { return numLines; } + json& getData() { return data; } + std::string outputBalance(const std::string& balance) + { + auto temp = std::string(balance); + while (temp.size() > BALANCE_LENGTH - 1) { + auto part = temp.substr(0, BALANCE_LENGTH); + output << part << std::endl; + output << setw(52) << " "; + temp = temp.substr(BALANCE_LENGTH); + } + return temp; + } + void list_datasets() + { + auto datasets = platform::Datasets(false, platform::Paths::datasets()); + locale mylocale(std::cout.getloc(), new separated_datasets); + locale::global(mylocale); + output.imbue(mylocale); + output << Colors::GREEN() << " # Dataset Sampl. Feat. Cls Balance" << std::endl; + std::string balanceBars = std::string(BALANCE_LENGTH, '='); + output << "=== ============================== ====== ===== === " << balanceBars << std::endl; + int num = 0; + for (const auto& dataset : datasets.getNames()) { + auto color = num % 2 ? Colors::CYAN() : Colors::BLUE(); + output << color << setw(3) << right << num++ << " "; + output << setw(30) << left << dataset << " "; + datasets.loadDataset(dataset); + auto nSamples = datasets.getNSamples(dataset); + output << setw(6) << right << nSamples << " "; + output << setw(5) << right << datasets.getFeatures(dataset).size() << " "; + output << setw(3) << right << datasets.getNClasses(dataset) << " "; + std::stringstream oss; + std::string sep = ""; + for (auto number : datasets.getClassesCounts(dataset)) { + oss << sep << std::setprecision(2) << fixed << (float)number / nSamples * 100.0 << "% (" << number << ")"; + sep = " / "; + } + auto balance = outputBalance(oss.str()); + output << balance << std::endl; + // Store data for Excel report + data[dataset] = json::object(); + data[dataset]["samples"] = nSamples; + data[dataset]["features"] = datasets.getFeatures(dataset).size(); + data[dataset]["classes"] = datasets.getNClasses(dataset); + data[dataset]["balance"] = oss.str(); + } + numLines = num + 2; + } + private: + std::stringstream output; + json data; + int numLines = 0; + }; +} + diff --git a/src/list/DatasetsExcel.cpp b/src/reports/DatasetsExcel.cpp similarity index 100% rename from src/list/DatasetsExcel.cpp rename to src/reports/DatasetsExcel.cpp diff --git a/src/list/DatasetsExcel.h b/src/reports/DatasetsExcel.h similarity index 100% rename from src/list/DatasetsExcel.h rename to src/reports/DatasetsExcel.h diff --git a/src/main/Result.cpp b/src/results/Result.cpp similarity index 100% rename from src/main/Result.cpp rename to src/results/Result.cpp diff --git a/src/main/Result.h b/src/results/Result.h similarity index 100% rename from src/main/Result.h rename to src/results/Result.h diff --git a/src/list/ResultsDataset.cpp b/src/results/ResultsDataset.cpp similarity index 100% rename from src/list/ResultsDataset.cpp rename to src/results/ResultsDataset.cpp diff --git a/src/list/ResultsDataset.h b/src/results/ResultsDataset.h similarity index 97% rename from src/list/ResultsDataset.h rename to src/results/ResultsDataset.h index 4ff542c..0d9bac3 100644 --- a/src/list/ResultsDataset.h +++ b/src/results/ResultsDataset.h @@ -3,7 +3,7 @@ #include #include #include -#include "main/Result.h" +#include "results/Result.h" namespace platform { using json = nlohmann::json; class ResultsDataset { diff --git a/src/results/ResultsDatasetConsole.hpp b/src/results/ResultsDatasetConsole.hpp new file mode 100644 index 0000000..3d9fa91 --- /dev/null +++ b/src/results/ResultsDatasetConsole.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include +#include +#include "common/Colors.h" +#include "results/ResultsDataset.h" + +namespace platform { + class ResultsDatasetsConsole { + public: + ResultsDatasetsConsole() = default; + ~ResultsDatasetsConsole() = default; + std::string getOutput() const { return output.str(); } + int getNumLines() const { return numLines; } + json& getData() { return data; } + void list_results(const std::string& dataset, const std::string& score, const std::string& model) + { + auto results = platform::ResultsDataset(dataset, model, score); + results.load(); + results.sortModel(); + if (results.empty()) { + std::cerr << Colors::RED() << "No results found for dataset " << dataset << " and model " << model << Colors::RESET() << std::endl; + exit(1); + } + int maxModel = results.maxModelSize(); + int maxHyper = results.maxHyperSize(); + double maxResult = results.maxResultScore(); + // Build data for the Report + json data = json::object(); + data["results"] = json::array(); + data["max_models"] = json::object(); // Max score per model + for (const auto& result : results) { + auto results = result.getData(); + if (!data["max_models"].contains(result.getModel())) { + data["max_models"][result.getModel()] = 0; + } + for (const auto& item : results["results"]) { + if (item["dataset"] == dataset) { + + // Store data for Excel report + json res = json::object(); + res["date"] = result.getDate(); + res["time"] = result.getTime(); + res["model"] = result.getModel(); + res["score"] = item["score"].get(); + res["hyperparameters"] = item["hyperparameters"].dump(); + data["results"].push_back(res); + if (item["score"].get() > data["max_models"][result.getModel()]) { + data["max_models"][result.getModel()] = item["score"].get(); + } + break; + } + } + } + // + // List the results + // + output << Colors::GREEN() << "Results of dataset " << dataset << " - for " << model << " model" << std::endl; + output << "There are " << results.size() << " results" << std::endl; + output << Colors::GREEN() << " # " << std::setw(maxModel + 1) << std::left << "Model" << "Date Time Score Hyperparameters" << std::endl; + output << "=== " << std::string(maxModel, '=') << " ========== ======== =========== " << std::string(maxHyper, '=') << std::endl; + numLines = 4; + auto i = 0; + for (const auto& item : data["results"]) { + auto color = (i % 2) ? Colors::BLUE() : Colors::CYAN(); + auto score = item["score"].get(); + color = score == data["max_models"][item["model"].get()] ? Colors::YELLOW() : color; + color = score == maxResult ? Colors::RED() : color; + output << color << std::setw(3) << std::fixed << std::right << i++ << " "; + output << std::setw(maxModel) << std::left << item["model"].get() << " "; + output << color << item["date"].get() << " "; + output << color << item["time"].get() << " "; + output << std::setw(11) << std::setprecision(9) << std::fixed << score << " "; + output << item["hyperparameters"].get() << std::endl; + numLines++; + } + data["dataset"] = dataset; + data["score"] = score; + data["model"] = model; + data["lengths"]["maxModel"] = maxModel; + data["lengths"]["maxHyper"] = maxHyper; + data["maxResult"] = maxResult; + } + private: + std::stringstream output; + json data; + int numLines = 0; + }; +} + + + + diff --git a/src/list/ResultsDatasetExcel.cpp b/src/results/ResultsDatasetExcel.cpp similarity index 100% rename from src/list/ResultsDatasetExcel.cpp rename to src/results/ResultsDatasetExcel.cpp diff --git a/src/list/ResultsDatasetExcel.h b/src/results/ResultsDatasetExcel.h similarity index 100% rename from src/list/ResultsDatasetExcel.h rename to src/results/ResultsDatasetExcel.h