chore: auto-commit (43 archivos)

- .mcp.json
- bash/functions/infra/write_mcp_jupyter_config.md
- bash/functions/infra/write_mcp_jupyter_config.sh
- cpp/CMakeLists.txt
- cpp/apps/chart_demo
- cpp/apps/shaders_lab
- cpp/functions/gfx/gl_framebuffer.cpp
- cpp/functions/gfx/gl_framebuffer.h
- cpp/functions/gfx/gl_framebuffer.md
- cpp/functions/gfx/mesh_gpu.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 17:28:47 +02:00
parent a2efdcf003
commit fd5787c55f
44 changed files with 3924 additions and 64 deletions
@@ -0,0 +1,80 @@
---
name: clickhouse_insert_rows
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def clickhouse_insert_rows(base_url: str, table: str, rows: list[dict], *, user: str = 'default', password: str = '', database: str = 'analytics', timeout: float = 30.0) -> int"
description: "Inserta una lista de dicts en ClickHouse via HTTP (puerto 8123) usando el formato JSONEachRow. Retorna el numero de filas enviadas."
tags: [clickhouse, analytics, http, insert, ingest, etl]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [json, urllib.request, urllib.parse, urllib.error]
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/infra/clickhouse_insert_rows.py"
params:
- name: base_url
desc: "URL base del servidor ClickHouse sin trailing slash. Ej: 'http://127.0.0.1:18123'. Para tunel SSH, apunta al puerto local reenviado."
- name: table
desc: "Nombre de la tabla destino, con o sin prefijo de base de datos. Ej: 'analytics.gnula_movies' o 'gnula_movies'."
- name: rows
desc: "Lista de dicts a insertar. Cada dict se serializa como una linea JSON. Las claves deben coincidir con columnas existentes; columnas ausentes usan DEFAULT."
- name: user
desc: "Usuario ClickHouse para autenticacion via header X-ClickHouse-User (default: 'default')."
- name: password
desc: "Contrasena ClickHouse para autenticacion via header X-ClickHouse-Key (default: cadena vacia)."
- name: database
desc: "Base de datos ClickHouse enviada como parametro de query (default: 'analytics')."
- name: timeout
desc: "Timeout de socket en segundos (default: 30.0)."
output: "Entero con el numero de filas insertadas (len(rows)). Retorna 0 si rows esta vacio sin contactar el servidor."
---
## Ejemplo
```python
from infra import clickhouse_insert_rows
n = clickhouse_insert_rows(
"http://127.0.0.1:18123",
"analytics.gnula_movies",
[
{
"snapshot_ts": "2026-05-30 14:00:00",
"href": "/pelicula/avatar-el-camino-del-agua",
"title": "Avatar: El camino del agua",
"year": 2022,
"flags": "es.png",
"lang_es": 1,
"status": "pending",
"in_library": 0,
"detected_at": "2026-05-30T14:00:00",
"downloaded_at": "",
}
],
user="analytics",
password="secret",
database="analytics",
)
print(f"Inserted {n} rows")
```
## Cuando usarla
Cuando un ETL empuja snapshots o eventos a ClickHouse via HTTP (puerto 8123), incluyendo a traves de un tunel SSH a un ClickHouse interno no expuesto publicamente. Alternativa ligera (solo stdlib) a `clickhouse-driver` o `clickhouse-connect` cuando no se quieren dependencias externas.
## Gotchas
- `base_url` sin trailing slash: `"http://127.0.0.1:18123"`, no `"http://127.0.0.1:18123/"`.
- Fechas y datetimes deben pasarse como strings en formato que ClickHouse acepte (`"YYYY-MM-DD HH:MM:SS"`) o como enteros epoch. El caller formatea; esta funcion no convierte tipos.
- Arrays van como listas JSON nativas Python: `{"tags": ["drama", "sci-fi"]}`.
- Columnas ausentes en un dict usan el valor DEFAULT de la tabla (JSONEachRow ignora claves faltantes). No falla.
- Para tunel SSH: `ssh -L 18123:localhost:8123 user@host` y usar `base_url="http://127.0.0.1:18123"`.
- En caso de error HTTP, `ValueError` incluye el codigo y los primeros 500 caracteres del cuerpo — util para depurar errores de schema o SQL malformado.
- Lotes grandes: no hay batching interno. Si `rows` tiene miles de elementos, el body puede ser grande. Partir en chunks desde el caller si es necesario.