feat: init_jupyter_analysis v1.1.0 — soporte --project, --desc, --tags
Nueva funcion write_analysis_md_bash_infra genera analysis.md con frontmatter.
El pipeline ahora acepta --project para crear analisis directamente en
projects/{proyecto}/analysis/{nombre}/, valida que el proyecto exista,
genera analysis.md con dir_path correcto y ejecuta fn index al final.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-6
@@ -252,16 +252,26 @@ analysis/
|
|||||||
|
|
||||||
### Crear un analisis nuevo
|
### Crear un analisis nuevo
|
||||||
|
|
||||||
```bash
|
Un solo comando deja todo listo: carpetas, venv, paquetes, launcher, MCP, kernel startup, `analysis.md` con frontmatter y, si va en un proyecto, `fn index` final.
|
||||||
# Basico
|
|
||||||
fn run init_jupyter_analysis finanzas
|
|
||||||
|
|
||||||
# Con paquetes extra
|
```bash
|
||||||
|
# Analisis suelto (analysis/{nombre}/)
|
||||||
|
fn run init_jupyter_analysis finanzas
|
||||||
fn run init_jupyter_analysis ml scikit-learn torch
|
fn run init_jupyter_analysis ml scikit-learn torch
|
||||||
fn run init_jupyter_analysis duckdb polars duckdb
|
|
||||||
|
# Analisis dentro de un proyecto (projects/{proyecto}/analysis/{nombre}/)
|
||||||
|
fn run init_jupyter_analysis --project aurgi sale_prices --desc "Comprobacion precios"
|
||||||
|
fn run init_jupyter_analysis --project fn_monitoring coverage polars --tags "monitoring,coverage"
|
||||||
```
|
```
|
||||||
|
|
||||||
El pipeline `init_jupyter_analysis_bash_pipelines` compone 8 funciones atomicas del registry.
|
Flags del pipeline:
|
||||||
|
- `--project <nombre>` — crea el analisis dentro de `projects/{nombre}/analysis/` y ejecuta `fn index` al final. El proyecto debe existir (`projects/{nombre}/project.md`).
|
||||||
|
- `--desc "..."` — descripcion que se escribe en el frontmatter de `analysis.md`.
|
||||||
|
- `--tags "a,b,c"` — tags CSV que se escriben en el frontmatter.
|
||||||
|
|
||||||
|
**NUNCA** uses `mv` para mover un analisis de `analysis/` a `projects/{proyecto}/analysis/` despues de crearlo. Al mover, el `.venv/bin/activate` queda con el path antiguo hardcodeado y el launcher falla con `ERROR: jupyter-collaboration no esta instalado`. Si esto pasa: `rm -rf .venv && uv sync` dentro del directorio nuevo. La forma correcta es siempre crear con `--project` desde el inicio.
|
||||||
|
|
||||||
|
El pipeline `init_jupyter_analysis_bash_pipelines` (v1.1.0) compone 9 funciones atomicas del registry.
|
||||||
|
|
||||||
### Usar un analisis
|
### Usar un analisis
|
||||||
|
|
||||||
|
|||||||
@@ -53,17 +53,15 @@ mkdir -p ~/vaults/{vault_name}/{raw,processed,exports}
|
|||||||
ln -s ~/vaults/{vault_name} projects/{nombre}/vaults/{vault_name}
|
ln -s ~/vaults/{vault_name} projects/{nombre}/vaults/{vault_name}
|
||||||
# Crear vault.yaml con la entrada
|
# Crear vault.yaml con la entrada
|
||||||
|
|
||||||
# 4. Crear analysis dentro del proyecto
|
# 4. Crear analysis dentro del proyecto (un solo comando; ya indexa)
|
||||||
fn run init_jupyter_analysis {nombre_analysis} [paquetes...]
|
fn run init_jupyter_analysis --project {nombre} {nombre_analysis} --desc "..." [paquetes...]
|
||||||
mv analysis/{nombre_analysis} projects/{nombre}/analysis/
|
|
||||||
# Crear analysis.md con dir_path correcto
|
|
||||||
# Regenerar launcher y kernel startup:
|
|
||||||
source bash/functions/infra/write_jupyter_launcher.sh && write_jupyter_launcher projects/{nombre}/analysis/{tema}
|
|
||||||
source bash/functions/infra/write_jupyter_registry_kernel.sh && write_jupyter_registry_kernel projects/{nombre}/analysis/{tema}
|
|
||||||
|
|
||||||
# 5. Indexar
|
# 5. Verificar
|
||||||
fn index
|
|
||||||
fn show {nombre} # verifica el project y sus componentes
|
fn show {nombre} # verifica el project y sus componentes
|
||||||
|
|
||||||
|
# NUNCA: crear el analisis en analysis/ y luego mv al proyecto.
|
||||||
|
# Al mover se rompe el .venv (paths hardcodeados en activate).
|
||||||
|
# Si ya te paso: cd projects/{nombre}/analysis/{tema} && rm -rf .venv && uv sync
|
||||||
```
|
```
|
||||||
|
|
||||||
### Consultas utiles
|
### Consultas utiles
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
id: write_analysis_md_bash_infra
|
||||||
|
name: write_analysis_md
|
||||||
|
kind: function
|
||||||
|
lang: bash
|
||||||
|
domain: infra
|
||||||
|
version: 1.0.0
|
||||||
|
purity: impure
|
||||||
|
signature: "write_analysis_md(analysis_dir: string, name: string, description: string, tags_csv: string) -> string"
|
||||||
|
description: "Genera un archivo analysis.md con frontmatter valido para el registry. Calcula dir_path relativo a FN_REGISTRY_ROOT (o lo deduce buscando registry.db hacia arriba). Acepta tags como CSV."
|
||||||
|
tags: [analysis, frontmatter, registry, markdown]
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
returns: []
|
||||||
|
returns_optional: false
|
||||||
|
error_type: "error_go_core"
|
||||||
|
imports: []
|
||||||
|
example: "source write_analysis_md.sh && write_analysis_md projects/aurgi/analysis/ventas ventas 'Analisis de ventas' 'aurgi,ventas'"
|
||||||
|
tested: false
|
||||||
|
file_path: "bash/functions/infra/write_analysis_md.sh"
|
||||||
|
params:
|
||||||
|
- name: analysis_dir
|
||||||
|
desc: "Directorio del analisis donde se escribira analysis.md"
|
||||||
|
- name: name
|
||||||
|
desc: "Nombre del analisis (se usa en frontmatter name)"
|
||||||
|
- name: description
|
||||||
|
desc: "Descripcion breve del analisis"
|
||||||
|
- name: tags_csv
|
||||||
|
desc: "Tags separados por coma (opcional)"
|
||||||
|
output: "Ruta absoluta del analysis.md creado"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas
|
||||||
|
|
||||||
|
Forma parte del workflow de creacion rapida de analyses dentro de proyectos.
|
||||||
|
|
||||||
|
Requiere que `analysis_dir` exista fisicamente antes de llamar (para resolver path absoluto). Normalmente se llama dentro del pipeline `init_jupyter_analysis` despues de crear la estructura de carpetas.
|
||||||
|
|
||||||
|
El `dir_path` del frontmatter debe ser relativo a la raiz del registry para que `fn index` lo enlace correctamente al `project_id` si esta bajo `projects/{nombre}/analysis/`.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# write_analysis_md
|
||||||
|
# -----------------
|
||||||
|
# Genera un archivo analysis.md con frontmatter valido para el registry.
|
||||||
|
# El dir_path se calcula relativo a FN_REGISTRY_ROOT.
|
||||||
|
#
|
||||||
|
# USO (sourced):
|
||||||
|
# source write_analysis_md.sh
|
||||||
|
# write_analysis_md <analysis_dir> <name> <description> [tags_csv]
|
||||||
|
#
|
||||||
|
# EJEMPLOS:
|
||||||
|
# write_analysis_md projects/aurgi/analysis/sale_prices sale_prices "Comprobacion precios"
|
||||||
|
# write_analysis_md analysis/finanzas finanzas "Exploracion gastos" "finanzas,personal"
|
||||||
|
|
||||||
|
write_analysis_md() {
|
||||||
|
local analysis_dir="${1:-}"
|
||||||
|
local name="${2:-}"
|
||||||
|
local description="${3:-}"
|
||||||
|
local tags_csv="${4:-}"
|
||||||
|
|
||||||
|
if [ -z "$analysis_dir" ] || [ -z "$name" ]; then
|
||||||
|
echo "Uso: write_analysis_md <analysis_dir> <name> <description> [tags_csv]" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# dir_path relativo a FN_REGISTRY_ROOT
|
||||||
|
local registry_root="${FN_REGISTRY_ROOT:-}"
|
||||||
|
if [ -z "$registry_root" ]; then
|
||||||
|
# Intenta deducirlo: buscar registry.db hacia arriba
|
||||||
|
local probe="$(cd "$analysis_dir" && pwd)"
|
||||||
|
while [ "$probe" != "/" ] && [ ! -f "$probe/registry.db" ]; do
|
||||||
|
probe="$(dirname "$probe")"
|
||||||
|
done
|
||||||
|
registry_root="$probe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local abs_dir="$(cd "$analysis_dir" && pwd)"
|
||||||
|
local rel_dir="${abs_dir#${registry_root}/}"
|
||||||
|
|
||||||
|
# Construir array YAML de tags
|
||||||
|
local tags_yaml="[]"
|
||||||
|
if [ -n "$tags_csv" ]; then
|
||||||
|
tags_yaml="[$(echo "$tags_csv" | sed 's/,/, /g')]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local md_path="${analysis_dir}/analysis.md"
|
||||||
|
cat > "$md_path" << EOF
|
||||||
|
---
|
||||||
|
name: ${name}
|
||||||
|
lang: py
|
||||||
|
domain: datascience
|
||||||
|
description: "${description}"
|
||||||
|
tags: ${tags_yaml}
|
||||||
|
uses_functions: []
|
||||||
|
uses_types: []
|
||||||
|
framework: "jupyterlab"
|
||||||
|
entry_point: "notebooks/main.ipynb"
|
||||||
|
dir_path: "${rel_dir}"
|
||||||
|
repo_url: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notas
|
||||||
|
|
||||||
|
${description}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "$md_path"
|
||||||
|
}
|
||||||
@@ -3,10 +3,10 @@ name: init_jupyter_analysis
|
|||||||
kind: pipeline
|
kind: pipeline
|
||||||
lang: bash
|
lang: bash
|
||||||
domain: pipelines
|
domain: pipelines
|
||||||
version: "1.0.0"
|
version: "1.1.0"
|
||||||
purity: impure
|
purity: impure
|
||||||
signature: "init_jupyter_analysis(nombre: string, [...paquetes_extra: string]) -> void"
|
signature: "init_jupyter_analysis([--project <p>] [--desc <d>] [--tags <t>], nombre: string, [...paquetes_extra: string]) -> void"
|
||||||
description: "Inicializa un analisis Jupyter completo en analysis/{nombre}/ con venv, paquetes, launcher, MCP y reglas para agentes Claude. Acepta paquetes extra opcionales."
|
description: "Inicializa un analisis Jupyter completo con venv, paquetes, launcher, MCP, reglas Claude, kernel startup y analysis.md. Por defecto crea en analysis/{nombre}/; con --project crea en projects/{proyecto}/analysis/{nombre}/ y ejecuta fn index al final."
|
||||||
tags: [jupyter, analysis, setup, pipeline, bash, launcher]
|
tags: [jupyter, analysis, setup, pipeline, bash, launcher]
|
||||||
uses_functions:
|
uses_functions:
|
||||||
- assert_command_exists_bash_shell
|
- assert_command_exists_bash_shell
|
||||||
@@ -17,6 +17,7 @@ uses_functions:
|
|||||||
- write_mcp_jupyter_config_bash_infra
|
- write_mcp_jupyter_config_bash_infra
|
||||||
- write_claude_jupyter_rules_bash_infra
|
- write_claude_jupyter_rules_bash_infra
|
||||||
- write_jupyter_registry_kernel_bash_infra
|
- write_jupyter_registry_kernel_bash_infra
|
||||||
|
- write_analysis_md_bash_infra
|
||||||
uses_types: []
|
uses_types: []
|
||||||
returns: []
|
returns: []
|
||||||
returns_optional: false
|
returns_optional: false
|
||||||
@@ -27,7 +28,13 @@ params:
|
|||||||
desc: "nombre del análisis a crear"
|
desc: "nombre del análisis a crear"
|
||||||
- name: paquetes_extra
|
- name: paquetes_extra
|
||||||
desc: "paquetes Python adicionales a instalar (variadic, opcional)"
|
desc: "paquetes Python adicionales a instalar (variadic, opcional)"
|
||||||
output: "sin salida directa; estructura completa en analysis/{nombre}/"
|
- name: "--project"
|
||||||
|
desc: "nombre del proyecto bajo projects/ donde crear el analisis (opcional); si se omite, se crea en analysis/ raiz"
|
||||||
|
- name: "--desc"
|
||||||
|
desc: "descripcion breve del analisis (se escribe en analysis.md)"
|
||||||
|
- name: "--tags"
|
||||||
|
desc: "tags CSV que se escriben en analysis.md (opcional)"
|
||||||
|
output: "sin salida directa; estructura completa + analysis.md en el destino. Con --project, ejecuta fn index para registrar."
|
||||||
tested: false
|
tested: false
|
||||||
tests: []
|
tests: []
|
||||||
test_file_path: ""
|
test_file_path: ""
|
||||||
@@ -37,28 +44,30 @@ file_path: "bash/functions/pipelines/init_jupyter_analysis.sh"
|
|||||||
## Ejemplo
|
## Ejemplo
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Analisis basico
|
# Analisis suelto en analysis/
|
||||||
./init_jupyter_analysis.sh finanzas
|
|
||||||
|
|
||||||
# Con paquetes extra
|
|
||||||
./init_jupyter_analysis.sh duckdb polars duckdb
|
|
||||||
./init_jupyter_analysis.sh ml scikit-learn torch
|
|
||||||
|
|
||||||
# Via fn run
|
|
||||||
fn run init_jupyter_analysis finanzas
|
fn run init_jupyter_analysis finanzas
|
||||||
fn run init_jupyter_analysis ml scikit-learn torch
|
fn run init_jupyter_analysis ml scikit-learn torch
|
||||||
|
|
||||||
|
# Analisis dentro de un proyecto (un solo comando, todo resuelto)
|
||||||
|
fn run init_jupyter_analysis --project aurgi sale_prices --desc "Comprobacion precios"
|
||||||
|
fn run init_jupyter_analysis --project aurgi ventas polars --tags "aurgi,ventas"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Flujo
|
## Flujo
|
||||||
|
|
||||||
1. `assert_command_exists` — verifica que uv o python3 estan disponibles
|
1. `assert_command_exists` — verifica que uv o python3 estan disponibles
|
||||||
2. Crea estructura `analysis/{nombre}/notebooks/` y `analysis/{nombre}/data/`
|
2. Crea estructura `{destino}/notebooks/` y `{destino}/data/`
|
||||||
3. `init_uv_venv` — crea venv en `analysis/{nombre}/.venv/`
|
3. `init_uv_venv` — crea venv en `{destino}/.venv/`
|
||||||
4. `uv_add_packages` — instala jupyter, jupyterlab, jupyter-collaboration, jupyter-mcp-server, pandas, numpy, matplotlib + extras
|
4. `uv_add_packages` — instala jupyter, jupyterlab, jupyter-collaboration, jupyter-mcp-server, pandas, numpy, matplotlib + extras
|
||||||
5. `write_jupyter_launcher` — genera `run-jupyter-lab.sh` con modo colaborativo
|
5. `write_jupyter_launcher` — genera `run-jupyter-lab.sh` con modo colaborativo
|
||||||
6. `find_free_port` + `write_mcp_jupyter_config` — detecta puerto libre y genera `.mcp.json`
|
6. `find_free_port` + `write_mcp_jupyter_config` — detecta puerto libre y genera `.mcp.json`
|
||||||
7. `write_claude_jupyter_rules` — genera `.claude/CLAUDE.md` con reglas de agente
|
7. `write_claude_jupyter_rules` — genera `.claude/CLAUDE.md` con reglas de agente
|
||||||
8. `write_jupyter_registry_kernel` — genera IPython startup con `fn_query`, `fn_search`, `fn_code` y acceso a `python/functions/`
|
8. `write_jupyter_registry_kernel` — genera IPython startup con `fn_query`, `fn_search`, `fn_code` y acceso a `python/functions/`
|
||||||
|
9. `write_analysis_md` — genera `analysis.md` con frontmatter y `dir_path` correcto
|
||||||
|
|
||||||
|
Si se paso `--project`, al final ejecuta `fn index` para registrar el analisis en registry.db con `project_id` correcto.
|
||||||
|
|
||||||
|
Con `--project`, el destino es `projects/{proyecto}/analysis/{nombre}/` (requiere que `projects/{proyecto}/project.md` exista); sin el, es `analysis/{nombre}/`.
|
||||||
|
|
||||||
## Notas
|
## Notas
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# init_jupyter_analysis
|
# init_jupyter_analysis
|
||||||
# ----------------------
|
# ----------------------
|
||||||
# Inicializa un analisis Jupyter completo en analysis/{nombre}/.
|
# Inicializa un analisis Jupyter completo.
|
||||||
|
#
|
||||||
|
# Dos modos de uso:
|
||||||
|
# Suelto: analysis/{nombre}/
|
||||||
|
# Proyecto: projects/{proyecto}/analysis/{nombre}/ (con --project <proyecto>)
|
||||||
|
#
|
||||||
|
# En modo proyecto, tambien genera analysis.md con frontmatter correcto
|
||||||
|
# y ejecuta `fn index` al final para que el analisis quede registrado.
|
||||||
|
#
|
||||||
# Compone: assert_command_exists + find_free_port + init_uv_venv +
|
# Compone: assert_command_exists + find_free_port + init_uv_venv +
|
||||||
# uv_add_packages + write_jupyter_launcher +
|
# uv_add_packages + write_jupyter_launcher +
|
||||||
# write_mcp_jupyter_config + write_claude_jupyter_rules +
|
# write_mcp_jupyter_config + write_claude_jupyter_rules +
|
||||||
# write_jupyter_registry_kernel
|
# write_jupyter_registry_kernel + write_analysis_md
|
||||||
#
|
#
|
||||||
# USO:
|
# USO:
|
||||||
# ./init_jupyter_analysis.sh <nombre> [paquetes_extra...]
|
# ./init_jupyter_analysis.sh <nombre> [paquetes_extra...]
|
||||||
|
# ./init_jupyter_analysis.sh --project <proyecto> <nombre> [paquetes_extra...]
|
||||||
|
# ./init_jupyter_analysis.sh <nombre> --project <proyecto> [paquetes_extra...]
|
||||||
|
# ./init_jupyter_analysis.sh <nombre> [paquetes...] --desc "descripcion"
|
||||||
#
|
#
|
||||||
# EJEMPLOS:
|
# EJEMPLOS:
|
||||||
# ./init_jupyter_analysis.sh finanzas
|
# ./init_jupyter_analysis.sh finanzas
|
||||||
# ./init_jupyter_analysis.sh duckdb polars duckdb
|
# ./init_jupyter_analysis.sh --project aurgi sale_prices --desc "Comprobar precios"
|
||||||
# ./init_jupyter_analysis.sh ml scikit-learn torch
|
# ./init_jupyter_analysis.sh ml scikit-learn torch
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -29,49 +40,94 @@ source "$REGISTRY_ROOT/bash/functions/infra/write_jupyter_launcher.sh"
|
|||||||
source "$REGISTRY_ROOT/bash/functions/infra/write_mcp_jupyter_config.sh"
|
source "$REGISTRY_ROOT/bash/functions/infra/write_mcp_jupyter_config.sh"
|
||||||
source "$REGISTRY_ROOT/bash/functions/infra/write_claude_jupyter_rules.sh"
|
source "$REGISTRY_ROOT/bash/functions/infra/write_claude_jupyter_rules.sh"
|
||||||
source "$REGISTRY_ROOT/bash/functions/infra/write_jupyter_registry_kernel.sh"
|
source "$REGISTRY_ROOT/bash/functions/infra/write_jupyter_registry_kernel.sh"
|
||||||
|
source "$REGISTRY_ROOT/bash/functions/infra/write_analysis_md.sh"
|
||||||
|
|
||||||
# ── Argumentos ──────────────────────────────────────────────
|
# ── Parsing de argumentos (flags mezclados con posicionales) ─
|
||||||
|
|
||||||
|
PROJECT=""
|
||||||
|
NOMBRE=""
|
||||||
|
DESC=""
|
||||||
|
TAGS=""
|
||||||
|
EXTRA_PACKAGES=()
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--project)
|
||||||
|
PROJECT="$2"; shift 2 ;;
|
||||||
|
--desc|--description)
|
||||||
|
DESC="$2"; shift 2 ;;
|
||||||
|
--tags)
|
||||||
|
TAGS="$2"; shift 2 ;;
|
||||||
|
-h|--help)
|
||||||
|
grep "^#" "$0" | sed 's/^# \?//' ; exit 0 ;;
|
||||||
|
-*)
|
||||||
|
echo "Flag desconocido: $1" >&2 ; exit 1 ;;
|
||||||
|
*)
|
||||||
|
if [ -z "$NOMBRE" ]; then
|
||||||
|
NOMBRE="$1"
|
||||||
|
else
|
||||||
|
EXTRA_PACKAGES+=("$1")
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
NOMBRE="${1:-}"
|
|
||||||
if [ -z "$NOMBRE" ]; then
|
if [ -z "$NOMBRE" ]; then
|
||||||
echo "Uso: $0 <nombre> [paquetes_extra...]" >&2
|
echo "Uso: $0 [--project <proyecto>] <nombre> [paquetes_extra...]" >&2
|
||||||
echo " Ejemplo: $0 finanzas polars" >&2
|
echo " Ejemplo: $0 --project aurgi sale_prices --desc 'Comprobar precios'" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift
|
|
||||||
EXTRA_PACKAGES=("$@")
|
|
||||||
|
|
||||||
ANALYSIS_DIR="${REGISTRY_ROOT}/analysis/${NOMBRE}"
|
# ── Resolver directorio destino ──────────────────────────────
|
||||||
|
|
||||||
|
if [ -n "$PROJECT" ]; then
|
||||||
|
PROJECT_DIR="${REGISTRY_ROOT}/projects/${PROJECT}"
|
||||||
|
if [ ! -f "${PROJECT_DIR}/project.md" ]; then
|
||||||
|
echo "ERROR: El proyecto '${PROJECT}' no existe en projects/${PROJECT}/" >&2
|
||||||
|
echo " Creao primero con 'fn add -k project' o manualmente." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ANALYSIS_DIR="${PROJECT_DIR}/analysis/${NOMBRE}"
|
||||||
|
else
|
||||||
|
ANALYSIS_DIR="${REGISTRY_ROOT}/analysis/${NOMBRE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DESC" ]; then
|
||||||
|
DESC="Analisis ${NOMBRE}"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "════════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════════"
|
||||||
echo " INIT JUPYTER ANALYSIS: ${NOMBRE}"
|
echo " INIT JUPYTER ANALYSIS: ${NOMBRE}"
|
||||||
|
if [ -n "$PROJECT" ]; then
|
||||||
|
echo " Proyecto: ${PROJECT}"
|
||||||
|
fi
|
||||||
echo " Directorio: ${ANALYSIS_DIR}"
|
echo " Directorio: ${ANALYSIS_DIR}"
|
||||||
echo "════════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════════"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 1. Verificar herramientas ───────────────────────────────
|
# ── 1. Verificar herramientas ───────────────────────────────
|
||||||
|
|
||||||
echo "[1/8] Verificando herramientas..."
|
echo "[1/9] Verificando herramientas..."
|
||||||
assert_command_exists uv || assert_command_exists python3
|
assert_command_exists uv || assert_command_exists python3
|
||||||
echo " OK"
|
echo " OK"
|
||||||
|
|
||||||
# ── 2. Crear estructura de carpetas ─────────────────────────
|
# ── 2. Crear estructura de carpetas ─────────────────────────
|
||||||
|
|
||||||
echo "[2/8] Creando estructura..."
|
echo "[2/9] Creando estructura..."
|
||||||
mkdir -p "$ANALYSIS_DIR/notebooks" "$ANALYSIS_DIR/data"
|
mkdir -p "$ANALYSIS_DIR/notebooks" "$ANALYSIS_DIR/data"
|
||||||
echo " ${ANALYSIS_DIR}/notebooks/"
|
echo " ${ANALYSIS_DIR}/notebooks/"
|
||||||
echo " ${ANALYSIS_DIR}/data/"
|
echo " ${ANALYSIS_DIR}/data/"
|
||||||
|
|
||||||
# ── 3. Crear venv ───────────────────────────────────────────
|
# ── 3. Crear venv ───────────────────────────────────────────
|
||||||
|
|
||||||
echo "[3/8] Inicializando venv..."
|
echo "[3/9] Inicializando venv..."
|
||||||
venv_path=$(init_uv_venv "$ANALYSIS_DIR")
|
venv_path=$(init_uv_venv "$ANALYSIS_DIR")
|
||||||
echo " $venv_path"
|
echo " $venv_path"
|
||||||
|
|
||||||
# ── 4. Instalar paquetes ────────────────────────────────────
|
# ── 4. Instalar paquetes ────────────────────────────────────
|
||||||
|
|
||||||
echo "[4/8] Instalando paquetes..."
|
echo "[4/9] Instalando paquetes..."
|
||||||
BASE_PACKAGES=(jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib)
|
BASE_PACKAGES=(jupyter jupyterlab jupyter-collaboration jupyter-mcp-server pandas numpy matplotlib)
|
||||||
ALL_PACKAGES=("${BASE_PACKAGES[@]}" "${EXTRA_PACKAGES[@]}")
|
ALL_PACKAGES=("${BASE_PACKAGES[@]}" "${EXTRA_PACKAGES[@]}")
|
||||||
uv_add_packages "$ANALYSIS_DIR" "${ALL_PACKAGES[@]}"
|
uv_add_packages "$ANALYSIS_DIR" "${ALL_PACKAGES[@]}"
|
||||||
@@ -79,29 +135,46 @@ echo " Instalados: ${ALL_PACKAGES[*]}"
|
|||||||
|
|
||||||
# ── 5. Generar launcher ─────────────────────────────────────
|
# ── 5. Generar launcher ─────────────────────────────────────
|
||||||
|
|
||||||
echo "[5/8] Generando launcher..."
|
echo "[5/9] Generando launcher..."
|
||||||
launcher=$(write_jupyter_launcher "$ANALYSIS_DIR")
|
launcher=$(write_jupyter_launcher "$ANALYSIS_DIR")
|
||||||
echo " $launcher"
|
echo " $launcher"
|
||||||
|
|
||||||
# ── 6. Configurar MCP ───────────────────────────────────────
|
# ── 6. Configurar MCP ───────────────────────────────────────
|
||||||
|
|
||||||
echo "[6/8] Configurando MCP..."
|
echo "[6/9] Configurando MCP..."
|
||||||
port=$(find_free_port 8888 8899)
|
port=$(find_free_port 8888 8899)
|
||||||
mcp_config=$(write_mcp_jupyter_config "$ANALYSIS_DIR" "$port")
|
mcp_config=$(write_mcp_jupyter_config "$ANALYSIS_DIR" "$port")
|
||||||
echo " $mcp_config (puerto: $port)"
|
echo " $mcp_config (puerto: $port)"
|
||||||
|
|
||||||
# ── 7. Reglas para agentes ──────────────────────────────────
|
# ── 7. Reglas para agentes ──────────────────────────────────
|
||||||
|
|
||||||
echo "[7/8] Escribiendo reglas Claude..."
|
echo "[7/9] Escribiendo reglas Claude..."
|
||||||
rules=$(write_claude_jupyter_rules "$ANALYSIS_DIR")
|
rules=$(write_claude_jupyter_rules "$ANALYSIS_DIR")
|
||||||
echo " $rules"
|
echo " $rules"
|
||||||
|
|
||||||
# ── 8. Kernel startup con acceso al registry ────────────────
|
# ── 8. Kernel startup con acceso al registry ────────────────
|
||||||
|
|
||||||
echo "[8/8] Configurando kernel con acceso al registry..."
|
echo "[8/9] Configurando kernel con acceso al registry..."
|
||||||
kernel_startup=$(write_jupyter_registry_kernel "$ANALYSIS_DIR")
|
kernel_startup=$(write_jupyter_registry_kernel "$ANALYSIS_DIR")
|
||||||
echo " $kernel_startup"
|
echo " $kernel_startup"
|
||||||
|
|
||||||
|
# ── 9. analysis.md (+ fn index si es proyecto) ──────────────
|
||||||
|
|
||||||
|
echo "[9/9] Escribiendo analysis.md..."
|
||||||
|
export FN_REGISTRY_ROOT="$REGISTRY_ROOT"
|
||||||
|
md_path=$(write_analysis_md "$ANALYSIS_DIR" "$NOMBRE" "$DESC" "$TAGS")
|
||||||
|
echo " $md_path"
|
||||||
|
|
||||||
|
if [ -n "$PROJECT" ]; then
|
||||||
|
echo ""
|
||||||
|
echo " Indexando registry..."
|
||||||
|
if [ -x "${REGISTRY_ROOT}/fn" ]; then
|
||||||
|
( cd "$REGISTRY_ROOT" && ./fn index 2>&1 | tail -3 )
|
||||||
|
else
|
||||||
|
echo " WARN: binario 'fn' no encontrado en ${REGISTRY_ROOT}. Ejecuta 'fn index' manualmente."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Resumen ─────────────────────────────────────────────────
|
# ── Resumen ─────────────────────────────────────────────────
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -36,12 +36,16 @@ def _resolve_collab_username(server_url: str, token: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _notebook_exists(notebook_path: str, server_url: str, token: str) -> bool:
|
def _notebook_exists(notebook_path: str, server_url: str, token: str) -> bool:
|
||||||
"""Comprueba si un notebook existe en el servidor Jupyter via HEAD /api/contents."""
|
"""Comprueba si un notebook existe en el servidor Jupyter via GET /api/contents.
|
||||||
|
|
||||||
|
Usa GET con ?content=0 (metadata only). HEAD no es soportado universalmente
|
||||||
|
(Jupyter 4.x devuelve 405 Method Not Allowed en /api/contents/{path}).
|
||||||
|
"""
|
||||||
headers = {"Accept": "application/json"}
|
headers = {"Accept": "application/json"}
|
||||||
if token:
|
if token:
|
||||||
headers["Authorization"] = f"token {token}"
|
headers["Authorization"] = f"token {token}"
|
||||||
check_url = f"{server_url}/api/contents/{notebook_path}"
|
check_url = f"{server_url}/api/contents/{notebook_path}?content=0"
|
||||||
req = Request(check_url, headers=headers, method="HEAD")
|
req = Request(check_url, headers=headers, method="GET")
|
||||||
try:
|
try:
|
||||||
with urlopen(req, timeout=5):
|
with urlopen(req, timeout=5):
|
||||||
return True
|
return True
|
||||||
@@ -340,9 +344,9 @@ def jupyter_create_notebook(
|
|||||||
if token:
|
if token:
|
||||||
headers["Authorization"] = f"token {token}"
|
headers["Authorization"] = f"token {token}"
|
||||||
|
|
||||||
# Verificar si ya existe (HEAD request)
|
# Verificar si ya existe (GET con content=0; HEAD no esta soportado en Jupyter 4.x)
|
||||||
check_url = f"{server_url}/api/contents/{notebook_path}"
|
check_url = f"{server_url}/api/contents/{notebook_path}"
|
||||||
check_req = Request(check_url, headers=headers, method="HEAD")
|
check_req = Request(f"{check_url}?content=0", headers=headers, method="GET")
|
||||||
already_exists = False
|
already_exists = False
|
||||||
try:
|
try:
|
||||||
with urlopen(check_req, timeout=5):
|
with urlopen(check_req, timeout=5):
|
||||||
|
|||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user