927437a8d8
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>
131 lines
3.7 KiB
Python
131 lines
3.7 KiB
Python
"""Tests para add_xlsx_chart.
|
|
|
|
Modulos importados por path directo (sin tocar __init__.py). write_xlsx_sheets
|
|
escribe los datos; add_xlsx_chart les anade un grafico; reabrimos el libro y
|
|
verificamos que ws._charts contiene el chart.
|
|
"""
|
|
|
|
import importlib.util
|
|
import os
|
|
|
|
from openpyxl import load_workbook
|
|
|
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
def _load(name):
|
|
spec = importlib.util.spec_from_file_location(name, os.path.join(_HERE, f"{name}.py"))
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
add_xlsx_chart = _load("add_xlsx_chart").add_xlsx_chart
|
|
write_xlsx_sheets = _load("write_xlsx_sheets").write_xlsx_sheets
|
|
|
|
|
|
def _book_with_data(tmp_path):
|
|
out = str(tmp_path / "chart.xlsx")
|
|
write_xlsx_sheets(
|
|
out,
|
|
{
|
|
"Ventas": [
|
|
["Mes", "Unidades"],
|
|
["Ene", 120],
|
|
["Feb", 150],
|
|
["Mar", 90],
|
|
["Abr", 200],
|
|
],
|
|
},
|
|
)
|
|
return out
|
|
|
|
|
|
def test_add_bar_chart_reabre_y_verifica(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(
|
|
xlsx_path=out,
|
|
sheet_name="Ventas",
|
|
chart_type="bar",
|
|
data_range="B1:B5",
|
|
cats_range="A2:A5",
|
|
anchor="D2",
|
|
title="Unidades por mes",
|
|
x_title="Mes",
|
|
y_title="Unidades",
|
|
)
|
|
assert res["status"] == "ok"
|
|
assert res["chart_type"] == "bar"
|
|
assert res["sheet"] == "Ventas"
|
|
assert res["anchor"] == "D2"
|
|
|
|
wb = load_workbook(out)
|
|
ws = wb["Ventas"]
|
|
assert len(ws._charts) == 1
|
|
|
|
|
|
def test_add_line_chart(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(out, "Ventas", "line", "B1:B5", cats_range="A2:A5")
|
|
assert res["status"] == "ok"
|
|
wb = load_workbook(out)
|
|
assert len(wb["Ventas"]._charts) == 1
|
|
|
|
|
|
def test_add_pie_chart(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(out, "Ventas", "pie", "B2:B5", cats_range="A2:A5", anchor="D10")
|
|
assert res["status"] == "ok"
|
|
wb = load_workbook(out)
|
|
assert len(wb["Ventas"]._charts) == 1
|
|
|
|
|
|
def test_add_scatter_chart(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(
|
|
out, "Ventas", "scatter", data_range="B2:B5", cats_range="A2:A5"
|
|
)
|
|
assert res["status"] == "ok"
|
|
wb = load_workbook(out)
|
|
assert len(wb["Ventas"]._charts) == 1
|
|
|
|
|
|
def test_dos_charts_en_la_misma_hoja(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
assert add_xlsx_chart(out, "Ventas", "bar", "B1:B5", "A2:A5", "D2")["status"] == "ok"
|
|
assert add_xlsx_chart(out, "Ventas", "line", "B1:B5", "A2:A5", "D20")["status"] == "ok"
|
|
wb = load_workbook(out)
|
|
assert len(wb["Ventas"]._charts) == 2
|
|
|
|
|
|
def test_chart_type_invalido_devuelve_error(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(out, "Ventas", "donut", "B1:B5")
|
|
assert res["status"] == "error"
|
|
assert "chart_type invalido" in res["error"]
|
|
|
|
|
|
def test_hoja_inexistente_devuelve_error(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(out, "Fantasma", "bar", "B1:B5")
|
|
assert res["status"] == "error"
|
|
assert "no existe" in res["error"]
|
|
|
|
|
|
def test_libro_inexistente_devuelve_error():
|
|
res = add_xlsx_chart("/tmp/no_existe_seguro_987654.xlsx", "S", "bar", "B1:B5")
|
|
assert res["status"] == "error"
|
|
assert "no encontrado" in res["error"]
|
|
|
|
|
|
def test_data_range_invalido_devuelve_error(tmp_path):
|
|
out = _book_with_data(tmp_path)
|
|
res = add_xlsx_chart(out, "Ventas", "bar", "rango_basura")
|
|
assert res["status"] == "error"
|
|
assert "data_range invalido" in res["error"]
|
|
|
|
|
|
def test_xlsx_path_vacio_devuelve_error():
|
|
res = add_xlsx_chart("", "S", "bar", "B1:B5")
|
|
assert res["status"] == "error"
|