feat(browser): auto-commit con 178 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 18:22:23 +02:00
parent 7d100e7f3e
commit 763e06c127
178 changed files with 19917 additions and 317 deletions
@@ -0,0 +1,130 @@
---
id: build_join_graph_py_datascience
name: build_join_graph
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: pure
signature: "def build_join_graph(fk_candidates: list, tables: list = None) -> dict"
description: "Construye un grafo de relaciones inter-tabla a partir de FK candidatas (salida fk_candidates de infer_fk_containment_duckdb): nodos con grados y rol (fact/dimension/bridge/standalone), aristas por FK, hubs (candidatas a tabla de hechos) y un diagrama Mermaid graph LR pegable. Funcion pura, sin deps externas, no muta el input."
tags: [eda, relations, join, schema, graph, mermaid, star-schema, datascience]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
example: |
from datascience import build_join_graph
fks = [
{"from_table": "orders", "from_col": "customer_id",
"to_table": "customers", "to_col": "id",
"inclusion": 1.0, "cardinality": "many-to-one"},
{"from_table": "orders", "from_col": "product_id",
"to_table": "products", "to_col": "id",
"inclusion": 0.98, "cardinality": "many-to-one"},
]
g = build_join_graph(fks)
# g["hubs"] == ["orders"]; orders -> role "fact", customers/products -> "dimension"
print(g["mermaid"])
tested: true
tests:
- "test_star_schema_roles_and_hub"
- "test_two_edges_built"
- "test_mermaid_contains_tables_and_arrows"
- "test_bridge_role"
- "test_standalone_node_from_tables_list"
- "test_empty_list_does_not_crash"
- "test_none_input_does_not_crash"
- "test_malformed_entries_skipped"
- "test_does_not_mutate_input"
test_file_path: "python/functions/datascience/build_join_graph_test.py"
file_path: "python/functions/datascience/build_join_graph.py"
params:
- name: fk_candidates
desc: >
lista de dicts, cada uno una FK candidata con al menos las claves
from_table, from_col, to_table, to_col, inclusion, cardinality. Suele ser
la salida `fk_candidates` de infer_fk_containment_duckdb. Las claves se
leen de forma defensiva con .get(...); entradas que no son dict o que no
tienen from_table/to_table se ignoran sin fallar. None se trata como [].
- name: tables
desc: >
lista opcional de nombres de TODAS las tablas. Sirve para incluir como
nodos aislados (role "standalone") las tablas que no aparecen en ninguna
FK. Si es None, los nodos se derivan solo de las aristas.
output: >
dict con nodes (list[dict] con table, out_degree, in_degree, role donde role
es "fact"|"dimension"|"bridge"|"standalone"), edges (list[dict] con
from_table, from_col, to_table, to_col, inclusion, cardinality, una por FK
valida), mermaid (str con un diagrama `graph LR` pegable en un bloque
```mermaid, una arista por FK etiquetada `from_col->to_col`) y hubs (list[str]
de tablas con out_degree>0 ordenadas por out_degree descendente, candidatas a
tabla de hechos / star schema).
---
## Ejemplo
```python
from datascience import build_join_graph
# fk_candidates concreto: orders apunta a customers y a products (estrella).
fks = [
{"from_table": "orders", "from_col": "customer_id",
"to_table": "customers", "to_col": "id",
"inclusion": 1.0, "cardinality": "many-to-one"},
{"from_table": "orders", "from_col": "product_id",
"to_table": "products", "to_col": "id",
"inclusion": 0.98, "cardinality": "many-to-one"},
]
g = build_join_graph(fks)
g["hubs"] # ["orders"]
# nodes: orders -> role "fact" (out_degree 2, in_degree 0),
# customers/products -> role "dimension" (in_degree 1, out_degree 0)
print(g["mermaid"])
```
El campo `mermaid` se pega tal cual en un bloque ```mermaid:
```mermaid
graph LR
orders["orders"] -->|customer_id->id| customers["customers"]
orders["orders"] -->|product_id->id| products["products"]
```
## Cuando usarla
Cuando hayas inferido las foreign keys de una base de datos con
`infer_fk_containment_duckdb` (grupo `eda`) y necesites **visualizar el esquema
relacional**: ver de un vistazo que tabla es la de hechos (hub/star schema),
cuales son dimensiones y cuales quedan sueltas. Devuelve un diagrama Mermaid
pegable en docs, un report o un dashboard, mas el grafo en dict para razonar
sobre los grados (priorizar joins, detectar tablas puente, planear el modelo
dimensional). Es la capa de grafo sobre las FK crudas: lee las candidatas, no
toca la base de datos.
## Notas
Funcion pura, sin I/O ni dependencias externas (solo stdlib), no muta
`fk_candidates`. Tolera lista vacia o `None` (devuelve grafo vacio con un
mermaid minimo `graph LR` con nota `empty`) y entradas malformadas (no-dict o
sin from_table/to_table se ignoran).
Heuristica de `role` por nodo, basada solo en grados:
- **fact** — `out_degree > 0` y `in_degree == 0`: apunta a otras tablas y nadie
le apunta. Es la candidata a tabla de hechos.
- **dimension** — `in_degree > 0` y `out_degree == 0`: solo recibe referencias
(tabla maestra / catalogo).
- **bridge** — `out_degree > 0` e `in_degree > 0`: apunta y recibe (tabla puente
o asociativa de una relacion many-to-many).
- **standalone** — sin aristas (solo aparece si se paso en `tables`).
`hubs` ordena por `out_degree` descendente las tablas con `out_degree > 0`. Para
un star schema limpio, `hubs[0]` es la tabla de hechos. Los IDs de nodo en el
Mermaid se sanean (no-alfanumerico -> `_`) pero la etiqueta visible conserva el
nombre original de la tabla.
```