refactor(0036a): rename Table-expanded -> NodeGroups (paperwork)

Rename masivo sin cambio de comportamiento. Habilita 0036b-f que ya
asumen la nueva convencion.

Archivos:
- tableview.{cpp,h} -> node_groups.{cpp,h} (git mv para preservar history)
- CMakeLists.txt: tableview.cpp -> node_groups.cpp

Tipos:
- TableWindowState  -> NodeGroupsWindowState  (views.h)
- TableMetadata     -> NodeGroupsMeta         (node_groups.h)
- TablePageRow      -> NodeGroupsRow          (node_groups.h)

Campos AppState:
- table_windows         -> node_groups_windows
- table_node_counts     -> node_groups_counts
- toggle_expanded_id    -> toggle_nodegroups_id
- want_toggle_expanded  -> want_toggle_nodegroups

Funciones (window por contenedor — NO el panel generico Table):
- tableview_create / count / page / smoke_test / resolve_path /
  refresh_counts / list_columns / get_metadata / set_expanded /
  set_columns / promote_row / demote_row / ingest_file
  -> prefijo node_groups_*
- views_table_window         -> views_node_groups_window
- views_table_windows_sync   -> views_node_groups_windows_sync
- views_table_overlay        -> views_node_groups_overlay

Strings de UI:
- "Expand table" / "Collapse table" -> "Open NodeGroups" / "Close NodeGroups"
- Window title "<icon> <name>" -> "<icon> NodeGroups: <name>"
- Tooltip "(no expanded tables)" -> "(no open NodeGroups)"
- Logs [tableview_*] -> [node_groups_*]

Preservados intencionalmente (no son cambio de identificadores C++):
- CLI flag --test-tableview (cambiarlo seria cambio de behavior publico)
- Valor 'tableview' en columna entities.source (cambiarlo afectaria
  datos persistidos en BD)

NO tocado:
- Panel generico Table (views_table, panel_table, table_rows,
  table_show_all, table_search_buf, table_filter_*, table_col_filters,
  table_active_tab, TableRow, table_filter_group_*, etc.)
- issues/completed/* (historia)

Verificacion:
- Build C++ Linux + Windows: green sin warnings nuevos.
- pytest WSL: 89 passed.
- pytest Windows: 78 passed + 11 skipped.
- git grep audit: solo residuos en issues/ (historia) + CLI flag y
  source DB value preservados.

Refs: issues/0036a-rename-nodegroups.md
This commit is contained in:
2026-05-04 00:43:01 +02:00
parent 441a697abf
commit 810b564127
7 changed files with 163 additions and 161 deletions
+52 -52
View File
@@ -33,7 +33,7 @@
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
#include "tableview.h"
#include "node_groups.h"
#include "duckdb.h"
#include <cstdio>
@@ -707,15 +707,15 @@ static bool load_input(bool first_load) {
// Cache de conteos de Table nodes (issue 0010).
if (g_input.uri) {
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
ge::node_groups_refresh_counts(g_input.uri, &g_app.node_groups_counts);
int64_t total_rows = 0;
for (auto& kv : g_app.table_node_counts) total_rows += kv.second;
for (auto& kv : g_app.node_groups_counts) total_rows += kv.second;
std::fprintf(stdout,
"[graph_explorer] table counts refreshed: %zu tables, %lld total rows\n",
g_app.table_node_counts.size(), (long long)total_rows);
g_app.node_groups_counts.size(), (long long)total_rows);
// Sync de windows expandidas (issue 0011) — reabre las que el
// usuario tenia abiertas en la sesion previa (metadata.expanded=true).
ge::views_table_windows_sync(g_app, g_input.uri);
ge::views_node_groups_windows_sync(g_app, g_input.uri);
}
// Cache de la vista tabla (issue 0004) — pull bulk + neighbors desde grafo.
@@ -882,15 +882,15 @@ static void render_context_menu() {
const char* sql_id = ge::entity_index_lookup(g_idx, n.user_data);
if (is_table && sql_id) {
// Determinar estado expanded actual sin ir a BD: mira table_windows.
bool currently_expanded =
g_app.table_windows.find(sql_id) != g_app.table_windows.end();
const char* lbl_exp = currently_expanded
? TI_X " Collapse table"
: TI_TABLE " Expand table";
// Determinar estado actual sin ir a BD: mira node_groups_windows.
bool currently_open =
g_app.node_groups_windows.find(sql_id) != g_app.node_groups_windows.end();
const char* lbl_exp = currently_open
? TI_X " Close NodeGroups"
: TI_TABLE " Open NodeGroups";
if (ImGui::MenuItem(lbl_exp)) {
g_app.want_toggle_expanded = true;
g_app.toggle_expanded_id = sql_id;
g_app.want_toggle_nodegroups = true;
g_app.toggle_nodegroups_id = sql_id;
}
ImGui::Separator();
}
@@ -1580,7 +1580,7 @@ static void render() {
// Reaplica types.yaml + atlas. Sin esto, despues de cualquier
// mutacion los tipos pierden color/shape/icon (todo nodo vuelve a
// circulo gris). Issue: al promover desde tableview el Table
// circulo gris). Issue: al promover desde node_groups el Table
// dejaba de ser cuadrado.
if (!g_app.parsed_types.entities.empty() ||
!g_app.parsed_types.relations.empty()) {
@@ -1592,10 +1592,10 @@ static void render() {
}
// Refresh Table node counts (issue 0010).
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
ge::node_groups_refresh_counts(g_input.uri, &g_app.node_groups_counts);
// Sincroniza windows (issue 0011) por si una Table aparecio o desaparecio.
ge::views_table_windows_sync(g_app, g_input.uri);
ge::views_node_groups_windows_sync(g_app, g_input.uri);
// Refresh table cache (issue 0004).
std::vector<ge::EntityRowSnapshot> snap;
@@ -1688,30 +1688,30 @@ static void render() {
}
// ---- Table node UI fase 2 (issue 0011) ----
if (g_app.want_toggle_expanded && !g_app.toggle_expanded_id.empty()
if (g_app.want_toggle_nodegroups && !g_app.toggle_nodegroups_id.empty()
&& !g_input_path.empty()) {
std::string id = g_app.toggle_expanded_id;
bool currently = g_app.table_windows.find(id) != g_app.table_windows.end();
ge::tableview_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
ge::views_table_windows_sync(g_app, g_input_path.c_str());
g_app.want_toggle_expanded = false;
g_app.toggle_expanded_id.clear();
std::string id = g_app.toggle_nodegroups_id;
bool currently = g_app.node_groups_windows.find(id) != g_app.node_groups_windows.end();
ge::node_groups_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
ge::views_node_groups_windows_sync(g_app, g_input_path.c_str());
g_app.want_toggle_nodegroups = false;
g_app.toggle_nodegroups_id.clear();
}
// Cierre via X de la ventana -> bajar expanded en BD.
for (auto it = g_app.table_windows.begin(); it != g_app.table_windows.end(); ) {
for (auto it = g_app.node_groups_windows.begin(); it != g_app.node_groups_windows.end(); ) {
if (!it->second.open && !g_input_path.empty()) {
ge::tableview_set_expanded(g_input_path.c_str(),
ge::node_groups_set_expanded(g_input_path.c_str(),
it->first.c_str(), false);
it = g_app.table_windows.erase(it);
it = g_app.node_groups_windows.erase(it);
} else ++it;
}
// Refrescar la pagina si alguna window esta dirty.
for (auto& kv : g_app.table_windows) {
for (auto& kv : g_app.node_groups_windows) {
auto& w = kv.second;
if (!w.page_dirty) continue;
const auto& m = w.meta;
w.last_error.clear();
bool ok_count = ge::tableview_count(m.duckdb_path_abs.c_str(),
bool ok_count = ge::node_groups_count(m.duckdb_path_abs.c_str(),
m.table_name.c_str(),
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
&w.total_rows);
@@ -1725,14 +1725,14 @@ static void render() {
}
if (m.columns.empty()) {
std::vector<std::string> cols;
if (ge::tableview_list_columns(m.duckdb_path_abs.c_str(),
if (ge::node_groups_list_columns(m.duckdb_path_abs.c_str(),
m.table_name.c_str(), &cols)) {
ge::tableview_set_columns(g_input_path.c_str(),
ge::node_groups_set_columns(g_input_path.c_str(),
m.entity_id.c_str(), cols);
w.meta.columns = cols;
}
}
bool ok_page = ge::tableview_page(m.duckdb_path_abs.c_str(),
bool ok_page = ge::node_groups_page(m.duckdb_path_abs.c_str(),
m.table_name.c_str(), m.id_column.c_str(),
w.meta.columns,
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
@@ -1749,11 +1749,11 @@ static void render() {
}
if (g_app.want_promote_row && !g_app.promote_table_id.empty()
&& !g_input_path.empty()) {
ge::TableMetadata m;
if (ge::tableview_get_metadata(g_input_path.c_str(),
ge::NodeGroupsMeta m;
if (ge::node_groups_get_metadata(g_input_path.c_str(),
g_app.promote_table_id.c_str(), &m)) {
char new_id[128] = {};
if (ge::tableview_promote_row(g_input_path.c_str(),
if (ge::node_groups_promote_row(g_input_path.c_str(),
g_app.promote_table_id.c_str(),
m.duckdb_path_abs.c_str(),
m.table_name.c_str(),
@@ -1763,8 +1763,8 @@ static void render() {
new_id, sizeof(new_id))) {
std::fprintf(stdout, "[promote] %s -> %s\n",
g_app.promote_row_id.c_str(), new_id);
auto it = g_app.table_windows.find(g_app.promote_table_id);
if (it != g_app.table_windows.end()) it->second.page_dirty = true;
auto it = g_app.node_groups_windows.find(g_app.promote_table_id);
if (it != g_app.node_groups_windows.end()) it->second.page_dirty = true;
reload_after_mutation();
g_app.want_focus_entity = true;
g_app.focus_entity_id = new_id;
@@ -1776,10 +1776,10 @@ static void render() {
}
if (g_app.want_demote_entity && !g_app.demote_entity_id.empty()
&& !g_input_path.empty()) {
if (ge::tableview_demote_row(g_input_path.c_str(),
if (ge::node_groups_demote_row(g_input_path.c_str(),
g_app.demote_entity_id.c_str())) {
std::fprintf(stdout, "[demote] %s\n", g_app.demote_entity_id.c_str());
for (auto& kv : g_app.table_windows) kv.second.page_dirty = true;
for (auto& kv : g_app.node_groups_windows) kv.second.page_dirty = true;
reload_after_mutation();
}
g_app.want_demote_entity = false;
@@ -1806,17 +1806,17 @@ static void render() {
if (g_app.want_import) {
g_app.want_import = false;
g_app.import_error.clear();
std::string duck_abs = ge::tableview_resolve_path(
std::string duck_abs = ge::node_groups_resolve_path(
g_input_path.c_str(), g_app.import_duckdb_buf);
std::string err;
if (!ge::tableview_ingest_file(duck_abs.c_str(),
if (!ge::node_groups_ingest_file(duck_abs.c_str(),
g_app.import_path_buf,
g_app.import_table_buf,
ge::INGEST_AUTO, &err)) {
g_app.import_error = "Ingest failed: " + err;
} else {
char new_id[80] = {};
if (ge::tableview_create(g_input_path.c_str(),
if (ge::node_groups_create(g_input_path.c_str(),
g_app.import_table_buf,
g_app.import_duckdb_buf,
g_app.import_table_buf,
@@ -1923,7 +1923,7 @@ static void render() {
if (type_name && std::strcmp(type_name, "Group") == 0) is_group = true;
if (is_group && sql_id) {
// Drill-in: abrir tableview filtrada por group_id = sql_id.
// Drill-in: abrir Table panel filtrado por group_id = sql_id.
g_app.table_filter_group_id = sql_id;
const char* lbl = graph::graph_label(&g_graph, g_graph.nodes[n].label_idx);
g_app.table_filter_group_name = lbl ? lbl : sql_id;
@@ -2007,7 +2007,7 @@ static void render() {
&get_label_cb, nullptr);
}
// Table node overlay (issue 0010) — encima de las labels.
ge::views_table_overlay(g_app);
ge::views_node_groups_overlay(g_app);
}
ImGui::End();
} else {
@@ -2051,7 +2051,7 @@ static void render() {
ge::views_table(g_app);
// Table node windows (issue 0011) — una por Table expandida.
ge::views_table_window(g_app);
ge::views_node_groups_window(g_app);
ge::views_import_dataset_modal(g_app);
// Jobs panel (issue 0026) — flotante, dockeable.
@@ -2181,7 +2181,7 @@ int main(int argc, char** argv) {
return test_types_yaml_roundtrip(argv[++i]);
} else if (std::strcmp(a, "--test-duckdb") == 0 && i + 1 < argc) {
const char* p = argv[++i];
if (!ge::tableview_smoke_test(p)) {
if (!ge::node_groups_smoke_test(p)) {
std::fprintf(stderr, "[duckdb] smoke test FAILED for %s\n", p);
return 2;
}
@@ -2209,25 +2209,25 @@ int main(int argc, char** argv) {
duckdb_disconnect(&cn); duckdb_close(&db);
int64_t total = 0;
if (!ge::tableview_count(p, "people", nullptr, &total) || total != 1000000) {
std::fprintf(stderr, "[tableview_count] expected 1000000, got %lld\n",
if (!ge::node_groups_count(p, "people", nullptr, &total) || total != 1000000) {
std::fprintf(stderr, "[node_groups_count] expected 1000000, got %lld\n",
(long long)total);
return 2;
}
std::vector<std::string> cols = { "name", "age" };
std::vector<ge::TablePageRow> page;
if (!ge::tableview_page(p, "people", "id", cols, nullptr,
std::vector<ge::NodeGroupsRow> page;
if (!ge::node_groups_page(p, "people", "id", cols, nullptr,
nullptr, nullptr, 500000, 10, &page)) {
std::fprintf(stderr, "[tableview_page] failed\n");
std::fprintf(stderr, "[node_groups_page] failed\n");
return 2;
}
if (page.size() != 10) {
std::fprintf(stderr, "[tableview_page] expected 10 rows, got %zu\n",
std::fprintf(stderr, "[node_groups_page] expected 10 rows, got %zu\n",
page.size());
return 2;
}
std::fprintf(stdout,
"[tableview] OK — count=%lld, page[0]={id=%s, name=%s, age=%s}\n",
"[node_groups] OK — count=%lld, page[0]={id=%s, name=%s, age=%s}\n",
(long long)total, page[0].id.c_str(),
page[0].values.size() > 0 ? page[0].values[0].c_str() : "",
page[0].values.size() > 1 ? page[0].values[1].c_str() : "");