diff --git a/app.md b/app.md index b1fab9a..76cd35b 100644 --- a/app.md +++ b/app.md @@ -18,7 +18,7 @@ uses_types: [] framework: "imgui" entry_point: "main.cpp" dir_path: "apps/registry_dashboard" -repo_url: "" +repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/registry_dashboard" --- ## Arquitectura diff --git a/data.cpp b/data.cpp index 1b32d70..6c3ca29 100644 --- a/data.cpp +++ b/data.cpp @@ -1,6 +1,13 @@ #include "data.h" #include #include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif // Helper: execute a query and call row_fn for each row template @@ -52,13 +59,57 @@ void RegistryData::prepare_chart_data() { } } +// Copy file src to dst. Returns true on success. +static bool copy_file(const char* src, const char* dst) { +#ifdef _WIN32 + return CopyFileA(src, dst, FALSE) != 0; +#else + FILE* in = fopen(src, "rb"); + if (!in) return false; + FILE* out = fopen(dst, "wb"); + if (!out) { fclose(in); return false; } + char buf[65536]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), in)) > 0) + fwrite(buf, 1, n, out); + fclose(in); + fclose(out); + return true; +#endif +} + +// On Windows, if the path is a network/UNC path (like \\wsl.localhost\...), +// copy the DB to a local temp file so SQLite locking works correctly. +// Returns the path to use (may be the original or a temp copy). +static std::string ensure_local_db(const char* db_path) { +#ifdef _WIN32 + // Detect UNC / network paths + if (strncmp(db_path, "\\\\", 2) == 0 || strncmp(db_path, "//", 2) == 0) { + char temp_dir[MAX_PATH] = {0}; + GetTempPathA(MAX_PATH, temp_dir); + std::string local_path = std::string(temp_dir) + "fn_registry_dashboard.db"; + + fprintf(stdout, "Copying DB to local temp: %s\n", local_path.c_str()); + if (copy_file(db_path, local_path.c_str())) { + return local_path; + } + fprintf(stderr, "Warning: failed to copy DB locally, using original path\n"); + } +#endif + return db_path; +} + bool load_registry_data(const char* db_path, RegistryData& out) { + std::string effective_path = ensure_local_db(db_path); + sqlite3* db = nullptr; - if (sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) { - fprintf(stderr, "Cannot open %s: %s\n", db_path, sqlite3_errmsg(db)); + if (sqlite3_open_v2(effective_path.c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) { + fprintf(stderr, "Cannot open %s: %s\n", effective_path.c_str(), sqlite3_errmsg(db)); return false; } + sqlite3_busy_timeout(db, 3000); + // --- Counts --- query(db, "SELECT COUNT(*) FROM functions", [&](sqlite3_stmt* s) { out.stats.total_functions = col_int(s, 0); diff --git a/main.cpp b/main.cpp index 43b311a..13368a6 100644 --- a/main.cpp +++ b/main.cpp @@ -61,7 +61,7 @@ int main(int argc, char** argv) { fprintf(stdout, "Using: %s\n", g_db_path.c_str()); break; } - fprintf(stderr, "Not found: %s\n", candidate); + fprintf(stderr, "Not found: %s\n", candidate.c_str()); } if (g_db_path.empty()) { diff --git a/registry_dashboard.ps1 b/registry_dashboard.ps1 index d906aa0..ea2022e 100644 --- a/registry_dashboard.ps1 +++ b/registry_dashboard.ps1 @@ -1,6 +1,5 @@ $exe = Join-Path $PSScriptRoot "registry_dashboard.exe" & $exe ` - "\\wsl.localhost\Ubuntu\home\lucas\fn_registry\registry.db" ` - "\\wsl$\Ubuntu\home\lucas\fn_registry\registry.db" ` - "$PSScriptRoot\registry.db" + "\\wsl.localhost\Ubuntu-22.04\home\lucas\fn_registry\registry.db" ` + diff --git a/views.cpp b/views.cpp index 58c47be..0303d75 100644 --- a/views.cpp +++ b/views.cpp @@ -23,13 +23,11 @@ static std::vector to_cstr(const std::vector& v) { void draw_kpi_row(const RegistryStats& stats) { float tested_pct = stats.total_functions > 0 - ? 100.0f * stats.tested_functions / stats.total_functions - : 0.0f; + ? 100.0f * stats.tested_functions / stats.total_functions : 0.0f; float pure_pct = stats.total_functions > 0 - ? 100.0f * stats.pure_functions / stats.total_functions - : 0.0f; + ? 100.0f * stats.pure_functions / stats.total_functions : 0.0f; - dashboard_grid_begin(6, 12.0f); + dashboard_grid_begin(8, 8.0f); kpi_card("Functions", static_cast(stats.total_functions), 0, nullptr, 0, "%.0f"); dashboard_grid_next(); @@ -39,17 +37,21 @@ void draw_kpi_row(const RegistryStats& stats) { dashboard_grid_next(); kpi_card("Analysis", static_cast(stats.total_analysis), 0, nullptr, 0, "%.0f"); dashboard_grid_next(); - kpi_card("Tested %", tested_pct, 0, nullptr, 0, "%.1f%%"); + kpi_card("Unit Tests", static_cast(stats.total_unit_tests), 0, nullptr, 0, "%.0f"); dashboard_grid_next(); - kpi_card("Pure %", pure_pct, 0, nullptr, 0, "%.1f%%"); + kpi_card("Proposals", static_cast(stats.total_proposals), 0, nullptr, 0, "%.0f"); + dashboard_grid_next(); + kpi_card("Tested", tested_pct, 0, nullptr, 0, "%.0f%%"); + dashboard_grid_next(); + kpi_card("Pure", pure_pct, 0, nullptr, 0, "%.0f%%"); dashboard_grid_end(); } -void draw_charts(RegistryData& data) { - dashboard_grid_begin(2, 12.0f); +void draw_charts(RegistryData& data, float height) { + dashboard_grid_begin(4, 8.0f); - if (dashboard_panel_begin("Functions by Language", 300, 280)) { + if (dashboard_panel_begin("By Language", 0, height)) { auto labels = to_cstr(data.lang_labels); if (!labels.empty()) bar_chart("##lang", labels.data(), data.lang_values.data(), static_cast(labels.size())); @@ -57,7 +59,7 @@ void draw_charts(RegistryData& data) { dashboard_panel_end(); dashboard_grid_next(); - if (dashboard_panel_begin("Functions by Domain", 300, 280)) { + if (dashboard_panel_begin("By Domain", 0, height)) { auto labels = to_cstr(data.domain_labels); if (!labels.empty()) bar_chart("##domain", labels.data(), data.domain_values.data(), static_cast(labels.size())); @@ -65,43 +67,21 @@ void draw_charts(RegistryData& data) { dashboard_panel_end(); dashboard_grid_next(); - if (dashboard_panel_begin("Purity", 250, 280)) { + if (dashboard_panel_begin("Purity", 0, height)) { const char* labels[] = {"Pure", "Impure"}; - float values[] = {static_cast(data.stats.pure_functions), static_cast(data.stats.impure_functions)}; + float values[] = {static_cast(data.stats.pure_functions), + static_cast(data.stats.impure_functions)}; pie_chart("##purity", labels, values, 2); } dashboard_panel_end(); dashboard_grid_next(); - if (dashboard_panel_begin("Kind", 250, 280)) { + if (dashboard_panel_begin("Kind", 0, height)) { auto labels = to_cstr(data.kind_labels); if (!labels.empty()) pie_chart("##kind", labels.data(), data.kind_values.data(), static_cast(labels.size())); } dashboard_panel_end(); - dashboard_grid_next(); - - if (dashboard_panel_begin("Activity (last 30 days)", 600, 280)) { - auto labels = to_cstr(data.date_labels); - if (!labels.empty()) - bar_chart("##dates", labels.data(), data.date_values.data(), static_cast(labels.size())); - else - ImGui::TextDisabled("No functions created in the last 30 days"); - } - dashboard_panel_end(); - dashboard_grid_next(); - - if (dashboard_panel_begin("Extras", 250, 280)) { - kpi_card("Unit Tests", static_cast(data.stats.total_unit_tests), 0, nullptr, 0, "%.0f"); - ImGui::Spacing(); - kpi_card("Proposals", static_cast(data.stats.total_proposals), 0, nullptr, 0, "%.0f"); - ImGui::Spacing(); - const char* t_labels[] = {"Tested", "Untested"}; - float t_values[] = {static_cast(data.stats.tested_functions), - static_cast(data.stats.total_functions - data.stats.tested_functions)}; - pie_chart("##tested", t_labels, t_values, 2); - } - dashboard_panel_end(); dashboard_grid_end(); } @@ -172,34 +152,30 @@ void draw_types_list(const std::vector& types) { void draw_dashboard(RegistryData& data) { fps_overlay(); - fullscreen_window_begin("##dashboard"); - // Header + float win_h = ImGui::GetContentRegionAvail().y; + + // Row 1: Header + KPIs (~80px) ImGui::Text("fn_registry Dashboard"); ImGui::SameLine(ImGui::GetWindowWidth() - 100); if (ImGui::Button("Reload")) { ImGui::GetIO().UserData = reinterpret_cast(1); } - ImGui::Separator(); - ImGui::Spacing(); - // KPIs draw_kpi_row(data.stats); - - ImGui::Spacing(); ImGui::Separator(); - ImGui::Spacing(); - // Charts - draw_charts(data); + // Row 2: Charts — take 35% of remaining height + float remaining = ImGui::GetContentRegionAvail().y; + float chart_h = remaining * 0.35f; + if (chart_h < 150.0f) chart_h = 150.0f; - ImGui::Spacing(); + draw_charts(data, chart_h); ImGui::Separator(); - ImGui::Spacing(); - // Tables in tabs + // Row 3: Tables — fill the rest if (ImGui::BeginTabBar("##tables")) { if (ImGui::BeginTabItem("Recent Functions")) { draw_recent_functions(data.recent_funcs); diff --git a/views.h b/views.h index 55f876f..7347e54 100644 --- a/views.h +++ b/views.h @@ -7,7 +7,7 @@ void draw_dashboard(RegistryData& data); // Individual views (called by draw_dashboard) void draw_kpi_row(const RegistryStats& stats); -void draw_charts(RegistryData& data); +void draw_charts(RegistryData& data, float height = 250.0f); void draw_recent_functions(const std::vector& funcs); void draw_apps_list(const std::vector& apps); void draw_analysis_list(const std::vector& analyses);