From 4e4f68886879ec73c591c277fb3a9ec6d18e8ffa Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 6 Sep 2024 11:09:51 +0200 Subject: [PATCH] Implement save to CSV (without validation) --- modules/exoplanetsexperttool/dataloader.cpp | 46 ++++++++ modules/exoplanetsexperttool/dataloader.h | 14 +++ modules/exoplanetsexperttool/dataviewer.cpp | 115 +++++++++++++++++++- modules/exoplanetsexperttool/dataviewer.h | 1 + 4 files changed, 175 insertions(+), 1 deletion(-) diff --git a/modules/exoplanetsexperttool/dataloader.cpp b/modules/exoplanetsexperttool/dataloader.cpp index d7092ec875..ba53ed56b0 100644 --- a/modules/exoplanetsexperttool/dataloader.cpp +++ b/modules/exoplanetsexperttool/dataloader.cpp @@ -46,6 +46,7 @@ #include #include #include +#include namespace { constexpr std::string_view _loggerCat = "ExoplanetsDataLoader"; @@ -461,4 +462,49 @@ std::vector DataLoader::loadData(const DataSettings& settings) { return planets; } +void DataLoader::saveData(const std::filesystem::path& targetPath, + const std::vector& allItems, + const std::vector& indices, + const std::vector& columns) +{ + // TODO: verify csv ending + + std::fstream f(targetPath, std::ios::out); + + if (!f.is_open()) { + LERROR(std::format("Failed writing data to file: '{}'", targetPath)); + return; + } + + if (!columns.empty()) { + f << ghoul::join(columns, ",") << std::endl; + } + else { + std::string line; + for (auto [key, _] : allItems.front().dataColumns) { + line += key + ","; + } + f << line.substr(0, line.size() - 1) << std::endl; + } + + for (const size_t& i : indices) { + const ExoplanetItem& item = allItems[i]; + + std::string line; + for (auto [_, value] : item.dataColumns) { + if (std::holds_alternative(value)) { + float v = std::get(value); + line += std::isnan(v) ? "" : ghoul::to_string(v); + } + else { + line += std::get(value); + } + line += ","; + } + f << line.substr(0, line.size() - 1) << std::endl; + } + + // TODO: handle indices empty +} + } // namespace openspace::exoplanets diff --git a/modules/exoplanetsexperttool/dataloader.h b/modules/exoplanetsexperttool/dataloader.h index 9fb13059e6..6387346ae1 100644 --- a/modules/exoplanetsexperttool/dataloader.h +++ b/modules/exoplanetsexperttool/dataloader.h @@ -37,6 +37,20 @@ public: static DataSettings loadDataSettingsFromJson(); static std::vector loadData(const DataSettings& settings); + + /** + * Save data to a CSV file. + * + * \param targetPath the filepath for where to save the dataset + * \param allItems the list of data items in the loaded dataset + * \param indices the indices which to save. If empty, save all + * \param columns the list of names for the columns to save in the dataset. + * If empty, save all + */ + static void saveData(const std::filesystem::path& targetPath, + const std::vector& allItems, + const std::vector& indices = {}, + const std::vector& columns = {}); }; } // namespace openspace::exoplanets diff --git a/modules/exoplanetsexperttool/dataviewer.cpp b/modules/exoplanetsexperttool/dataviewer.cpp index d33a1230ec..90ef5edfe7 100644 --- a/modules/exoplanetsexperttool/dataviewer.cpp +++ b/modules/exoplanetsexperttool/dataviewer.cpp @@ -526,6 +526,8 @@ void DataViewer::render() { handleDoubleClickHoveredPlanet(hoveredPlanet); if (ImGui::BeginMainMenuBar()) { + renderFileMenu(); + if (ImGui::BeginMenu("Windows")) { ImGui::MenuItem("Table", NULL, &showTable); ImGui::MenuItem("Filters", NULL, &showFilterSettingsWindow); @@ -689,7 +691,7 @@ void DataViewer::renderTableWindow(bool *open) { // Search table static char searchString[128] = ""; - ImGui::InputTextWithHint( + ImGui::InputTextWithHint( "##Query", "Search for an item by name here...", searchString, @@ -1154,6 +1156,117 @@ void DataViewer::updateFilteredRowsProperty(std::optional> c } } +void DataViewer::renderFileMenu() { + static bool showSaveCsvModal = false; + + // Menu + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("New", NULL, false, false)) { + // TODO: Create a settings.json file + } + if (ImGui::MenuItem("Open", NULL, false, false)) { + // TODO: Menu to load a new data file from disk + } + ImGui::MenuItem("Save CSV", NULL, &showSaveCsvModal); + ImGui::SameLine(); + view::helper::renderHelpMarker("Save the current filtered data items as a CSV file"); + if (ImGui::MenuItem("Save filters", NULL, false, false)) { + // TODO: Menu to save the current set of filters. + // To a settings .json file? Or a completely new file format? + } + ImGui::EndMenu(); + } + + // Modals for view + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_HorizontalScrollbar; + + if (showSaveCsvModal) { + ImGui::OpenPopup("Save CSV"); + if (ImGui::BeginPopupModal("Save CSV", NULL, flags)) { + ImGui::Text("Save the currently selected filtering to a CSV file."); + + static char name[128] = ""; + ImGui::InputText( + "Filename (.csv)", + name, + IM_ARRAYSIZE(name) + ); + + const char* defaultDir = absPath( + "${MODULE_EXOPLANETSEXPERTTOOL}/data" + ).string().c_str(); + + const float dirTextWidth = ImGui::CalcTextSize(defaultDir).x; + ImGui::SetNextItemWidth(dirTextWidth * 1.3f); + + static char dir[256] = ""; + ImGui::InputTextWithHint( + "Directory", + defaultDir, + dir, + IM_ARRAYSIZE(dir) + ); + + static bool incudeAllColumns = true; + ImGui::Checkbox("Include all columns", &incudeAllColumns); + ImGui::SameLine(); + view::helper::renderHelpMarker( + "If checked, include all data column in the original data file. " + "Otherwise, only include the selected columns." + ); + + // TODO: Also add option to save filtering separately + + view::helper::renderDescriptiveText(std::format( + "Will save {} rows with {} columns", + _filteredData.size(), + incudeAllColumns ? _data.front().dataColumns.size() :_columns.size() + ).c_str()); + + ImGui::Separator(); + + // Cancel + ImGuiIO& io = ImGui::GetIO(); + if (ImGui::Button("Cancel") || + ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_Escape])) + { + ImGui::CloseCurrentPopup(); + showSaveCsvModal = false; + } + ImGui::SameLine(); + // Save + if (ImGui::Button("Save") || + ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_Enter])) + { + std::filesystem::path fileName = std::filesystem::path(name); + std::filesystem::path directory = std::filesystem::path(dir); + if (directory.empty()) { + directory = std::filesystem::path(defaultDir); + } + + if (!fileName.has_extension()) { + fileName.replace_extension("csv"); + } + std::filesystem::path path = directory / fileName; + DataLoader::saveData( + path, + _data, + _filteredData, + !incudeAllColumns ? _columns : std::vector() + ); + + ImGui::CloseCurrentPopup(); + showSaveCsvModal = false; + } + ImGui::SetItemDefaultFocus(); + + ImGui::EndPopup(); + } + } +} + void DataViewer::renderSettingsMenuContent() { // OBS! These should match the default settings for the SGNs static bool useFixedWidth = false; diff --git a/modules/exoplanetsexperttool/dataviewer.h b/modules/exoplanetsexperttool/dataviewer.h index d4f66e346f..8940fd6b9e 100644 --- a/modules/exoplanetsexperttool/dataviewer.h +++ b/modules/exoplanetsexperttool/dataviewer.h @@ -123,6 +123,7 @@ private: void renderPlanetTooltip(int index) const; void handleDoubleClickHoveredPlanet(int index); + void renderFileMenu(); void renderSettingsMenuContent(); // Write the information about the rendered points to a file