feat(infra): grupo claude-fleet — FleetView TUI + orquestacion de Claudes
Sistema FleetView para centralizar la flota de procesos Claude Code vivos en una sola ventana kitty + tmux (socket aislado -L fleet) con un panel TUI: - list_claude_fleet (+ tipo claude_fleet): escanea ~/.claude/sessions + goals + runtime, valida procesos vivos (anti-PID-reciclado), join por sessionId. - list_resumable_claudes (+ tipo resumable_claude): sesiones cerradas reanudables. - wrappers tmux: tmux_new_claude_window (con --resume), tmux_swap_window_into_console (preserva ancho del sidebar), tmux_map_claude_panes. - launch_kittyclaude: comando entrypoint; instala atajos alt+flechas/enter/n/0/k/r, mouse on, remain-on-exit off; fija el ancho del sidebar con hooks. - docs/capabilities/claude-fleet.md + entrada en el INDEX. Incluye ademas funciones datascience en progreso (excel/duckdb/postgres) y ajustes varios de docs e infra de otra sesion, agrupados aqui para no perderlos. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
---
|
||||
name: excel_to_duckdb
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def excel_to_duckdb(xlsx_path: str, duckdb_path: str, table: str, sheet: str = None, mode: str = 'replace') -> dict"
|
||||
description: "Ingesta una hoja de un archivo .xlsx a una tabla DuckDB usando la extension nativa excel de DuckDB (camino activo, verificado en DuckDB 1.5.2). Abre el DuckDB destino en modo read-write (crea el archivo si no existe), carga la extension excel (INSTALL excel; LOAD excel;) y materializa la hoja con read_xlsx. El path del .xlsx y el nombre de la hoja se pasan como parametros posicionales (marcador ?) a read_xlsx, evitando inyeccion por esa via; el identificador de tabla destino se valida contra ^[A-Za-z_][A-Za-z0-9_]*$ y se cita. mode='replace' (default) hace CREATE OR REPLACE TABLE AS SELECT; mode='append' crea la tabla si no existe y luego INSERT INTO ... SELECT. Devuelve un dict sin lanzar (estilo del grupo duckdb): {status:'ok', table, row_count} en exito y {status:'error', error} en fallo. Depende de los paquetes duckdb (1.5.2) y, indirectamente, de la extension excel de DuckDB."
|
||||
tags: [duckdb, excel, xlsx, ingest, etl]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: [re, duckdb]
|
||||
params:
|
||||
- name: xlsx_path
|
||||
desc: "ruta al archivo .xlsx de origen. Debe existir y ser legible. Se pasa como parametro posicional a read_xlsx (no se interpola en el SQL)."
|
||||
- name: duckdb_path
|
||||
desc: "ruta al archivo DuckDB destino. Se abre en modo escritura, que crea el archivo si no existe. DuckDB es single-writer: si otro proceso lo tiene abierto en escritura falla con error de lock."
|
||||
- name: table
|
||||
desc: "nombre de la tabla destino. Se valida contra ^[A-Za-z_][A-Za-z0-9_]*$ antes de interpolarlo (CREATE/INSERT no admiten parametro para el nombre de tabla). Identificador invalido devuelve {status:'error'} sin tocar la base."
|
||||
- name: sheet
|
||||
desc: "nombre de la hoja a leer. None (default) lee la primera hoja del libro. Se pasa como parametro posicional sheet=? a read_xlsx."
|
||||
- name: mode
|
||||
desc: "'replace' (default) reemplaza la tabla entera con CREATE OR REPLACE TABLE AS SELECT; 'append' crea la tabla si no existe y luego inserta las filas con INSERT INTO ... SELECT. Otro valor devuelve {status:'error'}."
|
||||
output: "dict. En exito: {status:'ok', table:str, row_count:int} donde row_count es el numero de filas que tiene la tabla tras la ingesta. En error (sin lanzar): {status:'error', error:str}."
|
||||
tested: true
|
||||
tests:
|
||||
- "test_replace_ingesta_primera_hoja"
|
||||
- "test_seleccion_de_hoja_por_nombre"
|
||||
- "test_append_acumula_filas"
|
||||
- "test_replace_reemplaza_no_acumula"
|
||||
- "test_identificador_invalido_devuelve_status_error"
|
||||
- "test_mode_invalido_devuelve_status_error"
|
||||
- "test_xlsx_inexistente_devuelve_status_error"
|
||||
test_file_path: "python/functions/infra/excel_to_duckdb_test.py"
|
||||
file_path: "python/functions/infra/excel_to_duckdb.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys
|
||||
sys.path.insert(0, "python/functions")
|
||||
from infra.excel_to_duckdb import excel_to_duckdb
|
||||
|
||||
# Ingesta la primera hoja de un .xlsx a la tabla `ventas`, reemplazandola.
|
||||
res = excel_to_duckdb(
|
||||
"/tmp/informe_mensual.xlsx",
|
||||
"/tmp/almacen.duckdb",
|
||||
"ventas",
|
||||
mode="replace",
|
||||
)
|
||||
print(res) # {'status': 'ok', 'table': 'ventas', 'row_count': 1280}
|
||||
|
||||
# Ingesta una hoja concreta por nombre en modo append.
|
||||
res2 = excel_to_duckdb(
|
||||
"/tmp/informe_mensual.xlsx",
|
||||
"/tmp/almacen.duckdb",
|
||||
"detalle",
|
||||
sheet="Detalle",
|
||||
mode="append",
|
||||
)
|
||||
print(res2) # {'status': 'ok', 'table': 'detalle', 'row_count': 4096}
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando recibes datos en Excel (informes, exports, planillas manuales) y necesitas
|
||||
analizarlos o servirlos con SQL: es el primer eslabon del flujo
|
||||
`Excel -> DuckDB -> PostgreSQL`. Tras ingestar con esta funcion, usa
|
||||
`duckdb_query_readonly_py_infra` para analizar, `duckdb_table_schema_py_infra` para
|
||||
inspeccionar el schema inferido, y `duckdb_to_postgres_py_pipelines` para volcar a
|
||||
PostgreSQL y que Metabase/Grafana lo lean. mode='replace' para snapshots completos
|
||||
(refresco diario), mode='append' para acumular hojas sucesivas en una misma tabla.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Camino activo: extension nativa `excel` de DuckDB** (verificado en DuckDB 1.5.2:
|
||||
`read_xlsx` lee .xlsx y acepta `sheet=`). NO se usa el fallback openpyxl. Si en
|
||||
algun entorno la extension fallara, habria que reactivar un fallback openpyxl (no
|
||||
presente hoy) — documentar el cambio aqui si ocurre.
|
||||
- **`INSTALL excel` necesita red la primera vez** por conexion: descarga la extension
|
||||
del repositorio de extensiones de DuckDB. Una vez instalada queda cacheada en el
|
||||
home de DuckDB y `LOAD excel` funciona offline. En un entorno sin red y sin la
|
||||
extension cacheada, la ingesta falla con `{status:'error', ...}` (no se lanza).
|
||||
- Escritura real en disco (impura). DuckDB es single-writer: si otro proceso tiene
|
||||
`duckdb_path` abierto en escritura, `connect` falla con error de lock devuelto en
|
||||
el dict.
|
||||
- A diferencia de `read_only`, este modo **crea** el archivo DuckDB si no existe. Un
|
||||
`duckdb_path` con un directorio padre inexistente si falla y se reporta como error.
|
||||
- **Inferencia de tipos del .xlsx**: `read_xlsx` infiere los tipos de columna. Los
|
||||
numeros suelen inferirse como DOUBLE (incluso enteros), las fechas pueden quedar
|
||||
como VARCHAR segun el formato de la celda. Revisa el schema resultante con
|
||||
`duckdb_table_schema_py_infra` si el tipado importa aguas abajo.
|
||||
- En `mode='append'` el schema lo fija la **primera** ingesta (CREATE TABLE IF NOT
|
||||
EXISTS). Si una hoja posterior tiene columnas distintas, el INSERT puede fallar por
|
||||
desajuste de columnas; el error se devuelve en el dict.
|
||||
- El identificador `table` se valida (las CREATE/INSERT no parametrizan el nombre de
|
||||
tabla). Un nombre con caracteres fuera de `[A-Za-z0-9_]` devuelve
|
||||
`{status:'error', error:'invalid table identifier'}` sin tocar la base.
|
||||
Reference in New Issue
Block a user