Metadata-Version: 2.4
Name: orgm-bt
Version: 0.6.5
Summary: CLI for low-voltage electrical calculations, panel schedules, and HTML/PDF reports
Project-URL: Repository, https://github.com/osmargm1202/calc
Project-URL: Issues, https://github.com/osmargm1202/calc/issues
Keywords: electrical,low-voltage,NEC,panel-schedules,cli
Classifier: Environment :: Console
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: jinja2>=3.1.0
Requires-Dist: weasyprint<71,>=60.0
Requires-Dist: pillow>=11.0.0
Requires-Dist: reportlab>=4.4.4
Requires-Dist: sqlmodel>=0.0.14
Requires-Dist: alembic>=1.13.0
Requires-Dist: psycopg2-binary>=2.9.9
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: fastapi>=0.109.0
Requires-Dist: uvicorn[standard]>=0.27.0
Requires-Dist: requests>=2.32.5
Requires-Dist: python-dotenv>=1.0.1
Requires-Dist: pandas>=2.0.0
Requires-Dist: boto3>=1.34.0
Requires-Dist: rich>=13.9.4
Requires-Dist: tabulate>=0.9.0
Requires-Dist: questionary>=2.0.1
Requires-Dist: coloraide>=5.1
Requires-Dist: hsluv>=5.0.4
Requires-Dist: python-multipart>=0.0.20
Requires-Dist: customtkinter>=5.2.2
Requires-Dist: webdav4==0.11.0

# orgm-bt: Electrical Calculation Library

A Python library for electrical load calculations and balancing according to NEC standards. This library provides tools for importing AutoCAD data, calculating load balances, and generating PDF reports.

## uv

uv venv --directory /home/osmar/Code/calc
export UV_PROJECT_ENVIRONMENT=/home/osmar/Code/calc/.venv
export VIRTUAL_ENV=/home/osmar/Code/calc/.venv


## Features

- **Data Import**: Parse AutoCAD TXT files for circuits, CSV files for panels, and DU files for diagramas unifilares
- **Load Balancing**: Greedy algorithm for optimal circuit-to-phase assignment
- **Code Validation**: Support for predefined load codes with automatic normalization
- **Custom Loads**: Handle custom format codes (M-208-3F-5HP, AC-480-3F-10KVA)
- **Load Calculation**: Complete electrical load calculations according to NEC standards
- **Panel Hierarchy**: Automatic detection and validation of panel hierarchies (TRF → MPM → Panels)
- **PDF Generation**: Generate professional reports using WeasyPrint with multiple document types:
  - Memoria de cálculo
  - Cuadros de distribución
  - Alimentadores
  - Módulos MPM
  - Transformadores (TRF)
  - Equipos especiales
- **DU Processing**: Process and enrich Diagrama Unifilar (DU) files with calculated feeder data
- **Circuit Enrichment**: Enrich circuit files with calculated breaker and cable information
- **Database Storage**: PostgreSQL with SQLModel for project and company data
- **Local Command Manager**: Interactive CLI tool (`local.py`) for managing and executing calculation commands
- **Cloud Storage**: Upload generated files to R2/S3 storage with automatic URL generation

## Installation

1. Clone the repository:
```bash
git clone <repository-url>
cd calc
```

2. Install dependencies using uv:
```bash
uv sync
```

3. Set up environment variables:
```bash
cp .env.example .env
# Edit .env with your database connection details
```

4. Set up the database:
```bash
uv run alembic upgrade head
```

## Quick Start

### 1. CLI local instalable

La CLI no usa una ruta por defecto: no ejecuta cálculos hasta que el operador
configure explícitamente los activos y la carpeta de proyectos. Instálela con `uv tool`:

```bash
# Cuando la versión esté publicada en PyPI:
pip install orgm-bt
#
# Alternativa desde el repositorio:
uv tool install --from git+https://github.com/osmargm1202/calc.git orgm-bt

# Estas rutas son ejemplos: cada PC decide las suyas.
orgm-bt config setup \
  --assets-dir ~/Documentos/ORGM-Calc/Activos \
  --projects-dir ~/Documentos/ORGM-Calc/Proyectos
```

La configuración se guarda en `~/.config/orgm-calc/config.json`. `config setup`
siembra explícitamente los catálogos actuales, `Assets/`, y los perfiles ORGM de
empresa e ingeniero; no se crea nada hasta proporcionar ambas rutas.

`init` no inicializa un workspace. Se ejecuta desde la carpeta donde el agente de
IA trabajará y crea allí un `AGENTS.md` con el flujo de revisión de CSV, datos
faltantes, cálculo HTML y PDF:

```bash
cd ~/Documentos/ORGM-Calc
orgm-bt init
```

Cada proyecto guarda solo `ruta`, relativa a `projects_dir`:

```bash
orgm-bt profile create client acme --name "ACME"
orgm-bt project create "Nave China" \
  --relative-path "Nave China/Calculos/ELEC" \
  --client acme
orgm-bt calculate "Nave China/Calculos/ELEC" --revision-description "Emisión inicial"
```

El ingeniero es obligatorio y, si se omite `--engineer`, se utiliza el perfil
configurado. Empresa y cliente son opcionales: sin cliente se muestra la empresa,
si existe; sin ambos, se dejan vacíos. Para quitar una asignación existente,
use `project edit RUTA --company "" --client ""`. En el menú de creación, ORGM
aparece preseleccionado como ingeniero y empresa; puede seleccionar «Ninguno»
para empresa/cliente. Por CLI, la empresa solo se asigna si se proporciona.

`project import-local` sirve para importar proyectos de una configuración anterior
(`local.json`) al catálogo central. No mueve los archivos de cálculo y no se
necesita para calcular proyectos ya registrados.

```bash
orgm-bt project import-local /ruta/anterior/datos/local.json
```

Sin argumentos, `orgm-bt` abre el menú Questionary: cálculo, DU, PDF, finalizar,
crear perfiles/proyectos, editar `PANELES.csv`, listar y reconfigurar rutas.
El cálculo genera HTML primero; PDF es explícito y posterior con WeasyPrint o
Chromium/Google Chrome headless.

#### Editar perfiles y proyectos

```bash
orgm-bt profile list engineer
orgm-bt profile edit engineer ingeniero-miguel-estevez \
  --name "Miguel Estevez" --codia 411690 --email "correo@ejemplo.com"
orgm-bt project edit "Nave China/Calculos/ELEC" --norma NEC
orgm-bt project edit --help
```

La ayuda distingue el objetivo requerido de los campos editables opcionales.
En el menú se seleccionan los campos que se quieren cambiar; los perfiles se
eligen por nombre, con tabla de CODIA, correo y teléfono, sin escribir IDs.
Los valores actuales quedan preseleccionados. Cancelar no guarda cambios.

**Título del informe** (`--calculation`, antes «Cálculo realizado») es el texto
que identifica la memoria impresa; por defecto es «Memoria de cálculo BT».
No es una fórmula ni el número del cálculo.

#### Revisiones y control del documento

La revisión corresponde a la carpeta numérica de cálculo en `RESULTADOS/`.
Un borrador se recalcula en el mismo número. Si la última revisión está
finalizada, el próximo cálculo crea la siguiente; también puede elegir
explícitamente «Siguiente revisión» sin sobrescribir la anterior.

```bash
orgm-bt calculate "Nave China/Calculos/ELEC" --revision-description "Emisión inicial"
# Continuar el borrador actual:
orgm-bt calculate "Nave China/Calculos/ELEC"
# Cerrar la revisión 1:
orgm-bt finalize "Nave China/Calculos/ELEC" 1
# Crear la revisión siguiente:
orgm-bt calculate "Nave China/Calculos/ELEC" \
  --revision next --revision-description "Ajuste de cargas y alimentadores"
```

En una terminal interactiva se solicita la descripción de cada revisión nueva.
En automatizaciones debe proporcionarse `--revision-description`; una ejecución
fallida no se incorpora al historial de cálculos terminados.

El menú de edición muestra la revisión actual y la siguiente. Elegir la
siguiente prepara la intención; la revisión real del catálogo se actualiza
solo tras un cálculo exitoso. Los documentos finalizados no se sobrescriben.

Cada versión calculada incluye `control-documento.html` y `version.json`.
El documento JSON editable y canónico se guarda fuera de `RESULTADOS/`, en
`assets_dir/Proyectos/<identificador>/control-documento.json`. Los controles
JSON heredados que se encuentren dentro de `RESULTADOS/` se respaldan una sola
vez en `legacy-revisions/` durante la migración y dejan de ser fuentes activas.
La hoja «Control del documento», accesible desde el menú de reportes e incluida
en la exportación PDF, muestra revisión, fecha, cambios, ingeniero y estado.
Las revisiones antiguas sin esos datos se conservan sin inventar descripciones.

### 2. Uso Directo con main.py

Ejecutar cálculos directamente con `main.py`:

```bash
# Ejemplo básico
uv run main.py --path /ruta/al/proyecto --output /ruta/salida

# Con parámetros adicionales
uv run main.py \
  --path /ruta/al/proyecto \
  --output /ruta/salida \
  --cliente "Nombre Cliente" \
  --empresa "Nombre Empresa" \
  --ing "Ing. Nombre" \
  --codia "12345" \
  --proyecto-id 1 \
  --url-logo-empresa "https://ejemplo.com/logo.png"

# Solo generar HTML (sin PDF)
uv run main.py --path /ruta/al/proyecto --html

# Modo blanco y negro
uv run main.py --path /ruta/al/proyecto --bn

# Especificar tamaños de página personalizados
uv run main.py \
  --path /ruta/al/proyecto \
  --alimentadores "11,17" \
  --cuadros "36,24" \
  --modulos-trf "24,18"
```

### 3. Importar Datos a Base de Datos

Create a project and import circuit/panel data:

```bash
# Create a new project
uv run python scripts/import_data.py --create-project "My Project"

# Import circuits from TXT file
uv run python scripts/import_data.py --project-id 1 --import-circuits proy/CIRCUITOS/D\ -\ BT.txt

# Import panels from CSV file
uv run python scripts/import_data.py --project-id 1 --import-panels proy/PANELES/PANELES.csv
```

### 4. Estructura de Directorios Requerida

El proyecto espera la siguiente estructura en el directorio base (`--path`):

```
proyecto/
├── CIRCUITOS/
│   └── D - BT.txt          # Archivo de circuitos (TXT tab-separated)
├── PANELES/
│   └── PANELES.csv         # Archivo de paneles (CSV)
└── DU/
    ├── TRF1.txt             # Diagrama del TRF (UTF-8 TSV)
    ├── MPM1.txt             # Diagrama de MPM o panel
    ├── TOTALIZADOR-TRF1.txt  # Totalizador (DU-facing TTL; interno TOTALIZADOR)
    ├── INV-01.txt           # Inversor (DU-facing INV; interno INVERSOR)
    └── ...                  # Un archivo por equipo (TXT UTF-8)
```

Los archivos generados se guardan en el directorio de salida especificado con `--output`.

## Scripts Reference

### `scripts/test_calculations.py`
Comprehensive test script that:
- Initializes the database
- Tests individual services (parser, validator, balanceador)
- Runs complete workflow: import → balance → PDF generation

### `scripts/import_data.py`
Data import utility with options:
- `--create-project NAME`: Create new project
- `--list-projects`: List all projects
- `--import-circuits FILE`: Import circuits from TXT
- `--import-panels FILE`: Import panels from CSV
- `--project-id ID`: Specify project ID
- `--clear-existing`: Clear existing data before import

### `scripts/generate_pdfs.py`
PDF generation utility with options:
- `--list-projects`: List all projects
- `--project-id ID`: Specify project ID
- `--output FILE`: Single PDF output file
- `--output-dir DIR`: Directory for multiple PDFs
- `--calculate-balance`: Calculate balance before PDF generation
- `--all-pdfs`: Generate all available PDF types

## Data Formats

### Circuit TXT Format
Tab-separated values (TSV) file con información de circuitos eléctricos. El archivo puede contener múltiples columnas:

**Columnas principales requeridas:**
- `HANDLE`: Identificador único del circuito
- `BLOCKNAME`: Nombre del bloque (generalmente "BT - CIRCUITO")
- `PANEL`: Nombre del panel al que pertenece el circuito
- `CANTIDAD`: Cantidad de elementos del circuito
- `CODIGO` o `CARGA`: Código de carga (TMC, IL, A/A, M-208-3F-5HP, etc.)
- `AREA`: Área o ubicación del circuito

**Columnas opcionales:**
- `ALIMENTADOR`: Identificador del alimentador
- `CIRCUITO`: Número de circuito
- `DISTANCIA`: Distancia en metros
- `FASE`: Número de fases
- `POTENCIA`: Potencia en VA/W
- `FACTOR`: Factor de potencia
- `TIPO`: Tipo de carga. Use `SERVICIOS_AUXIALIARES` para calcular esa carga a FD=1.
- `TENSIÓN`: Tensión nominal
- `CANALIZACION`: Tipo de canalización (`PVC`, `PVC SCH40`, `EMT`, `IMC`, `RMC`, `ENT`)
- `BREAKER_MINIMO`: Breaker mínimo requerido
- `CABLE_MINIMO`: Cable mínimo requerido

**Columnas custom** (para especificar valores personalizados):
- `VA_CUSTOM`: Potencia custom en VA
- `TENSION_CUSTOM`: Tensión custom
- `FP_CUSTOM`: Factor de potencia custom
- `FASES_CUSTOM`: Número de fases custom

**Ejemplo:**
```
HANDLE	BLOCKNAME	PANEL	CANTIDAD	CODIGO	AREA	ALIMENTADOR	CIRCUITO
'1583E	BT - CIRCUITO	PBA	6	IL	GENERAL		
'158C5	BT - CIRCUITO	PBB	5	IL	GENERAL		
```

### Panel CSV Format
Archivo CSV con información de la jerarquía de paneles y sus características. Define la estructura de alimentación desde transformadores hasta paneles finales.

**Columnas requeridas:**
- `FUENTE`: Panel o transformador origen (TRF, MPM1, MPM2, etc.)
- `PANEL`: Identificador del panel destino
- `FASE`: Número de fases (1, 2, 3)
- `TENSION`: Tensión nominal (120, 208, 240, 277, 480)

**Columnas opcionales:**
- `DISTANCIA`: Distancia en metros desde la fuente
- `NUMERO`: Número del panel (puede ser rango como "101-102")
- `AREA`: Área o ubicación del panel
- `PROPOSITO`: Propósito del panel (residencial, comercial, etc.)
- `CANALIZACION`: Tipo de canalización (`PVC`, `PVC SCH40`, `EMT`, `IMC`, `RMC`, `ENT`)
- `TIPO`: Tipo de panel

Si la norma del proyecto es **CEPM**, importación desde `PANELES.csv` fuerza `CANALIZACION = PVC SCH40` para todos los alimentadores.
Importación de `CIRCUITOS`/cargas no aplica ese forzado y preserva el valor proporcionado.
- `POTENCIA`: Potencia nominal
- `POTENCIAL`: Potencial eléctrico
- `NEUTRO`: Configuración de neutro
- `TIERRA`: Configuración de tierra

**Ejemplo:**
```
FUENTE,PANEL,DISTANCIA,NUMERO,FASE,TENSION,AREA,PROPOSITO,CANALIZACION
MPM3,PBAC,20,1,2,208,AREA COMUN,area comun,EMT
MPM3,PBS,20,1,2,208,SOTANO,area comun,
MPM3,PBF,39,101,2,208,BLOQUE A NIVEL 1,residencial,
```

**Nota:** La jerarquía debe ser válida: TRF → MPM → Paneles. El sistema valida automáticamente que no haya ciclos y que todos los paneles tengan una fuente válida.

### DU (Diagrama Unifilar) TXT Format
Archivo tab-separated (TSV) UTF-8 para diagramas unifilares.

La carpeta `DU/` puede contener **múltiples** archivos `*.txt`. Cada archivo representa un equipo lógico y se procesa de forma independiente al ejecutar `main.py`.

**Semántica de nombre de archivo (sin extensión):**
- `TRF*` → tipo `TRF` (salida tipo resumen/export).
- `TRFS*`, `MB*`, `MPM*`, `INV*`/`INVERSOR*` → tipo `equipo` (llenado tabular).
- `TOTALIZADOR*` y `TTL*` → tipo `TTL` (DU-facing; alias interno `TOTALIZADOR*`).
- cualquier otro prefijo no reconocido → fallback tabular (si existe origen de alimentadores), o `DISPONIBLE` si no.

**Columnas requeridas:**
- `HANDLE`: Identificador único
- `BLOCKNAME`: Nombre del bloque
- `DESCRIPCION`: Descripción del elemento
- `AREA`: Área o ubicación
- `BREAKER`: Información del breaker (se llena automáticamente)
- `ALIMENTADOR`: ID del alimentador (se llena automáticamente)
- `FASE`: Número de fases (se llena automáticamente)
- `POTENCIAL`: Potencial eléctrico (se llena automáticamente)
- `PANEL`: Nombre del panel asociado

**Ejemplo:**
```
HANDLE	BLOCKNAME	DESCRIPCION	AREA	BREAKER	ALIMENTADOR	FASE	POTENCIAL	PANEL
'228AF	*U92			-	-	-		TRF1
'23117	*U93			-	-	-		PBA
```

**Nota:** Cada archivo generado conserva el nombre original del archivo de entrada bajo el directorio de salida `.../DU/`.

## Load Code Support

### Predefined Codes
- **TMC**: Tomacorrientes (120V, 1F)
- **IL**: Iluminación (120V, 1F)
- **A/A**: Aire acondicionado (208V, 3F)
- **M2F1**: Motor 2HP (208V, 3F)

### Custom Format Codes
- **M-208-3F-5HP**: Motor 5HP, 208V, 3-phase
- **AC-480-3F-10KVA**: AC unit 10KVA, 480V, 3-phase

### Code Normalization
Automatic normalization with warnings:
- TMCC → TMC
- LED → IL
- LED-P → IL

## Database Schema

### Core Tables
- `proyectos`: Project information
- `circuitos`: Circuit data with load information
- `paneles`: Panel specifications
- `jerarquia_alimentacion`: Panel hierarchy relationships

### Custom Fields
- `va_custom`: Custom VA rating
- `tension_custom`: Custom voltage
- `fp_custom`: Custom power factor
- `fases_custom`: Custom phase count

## Configuration Files

### `datos/cargas_predeterminadas.json`
Predefined load codes with electrical characteristics:
```json
{
  "TMC": {
    "descripcion": "TOMACORRIENTE",
    "potencia": 1800,
    "tension": 120,
    "fase": 1,
    "factor": 1.0,
    "tipo": "tc"
  }
}
```

### `datos/categorias.json`
Load categories for organization:
```json
{
  "tc": {"nombre": "Tomacorriente", "descripcion": "Tomacorrientes de uso general"},
  "il": {"nombre": "Iluminación", "descripcion": "Iluminación de uso general"},
  "aa": {"nombre": "Aire Acondicionado", "descripcion": "Sistemas de aire acondicionado"}
}
```

### `<proyecto>/cargas_proyecto.jsonc`

Cargas propias de un proyecto. Tienen prioridad sobre
`datos/cargas_predeterminadas.json`, así que un código definido aquí sustituye
al del catálogo general sin tocarlo.

Se editan desde el menú interactivo: `orgm-bt` → **Cargas del proyecto**, con
opciones para ver, agregar, editar y eliminar. La edición permite cambiar **un
solo dato** de una carga sin volver a capturar el resto.

El diálogo pregunta descripción, tipo, fases, tensión, potencia, conexión,
distancia, eficiencia, factor de potencia y si es individual/continua. Las
tensiones y conexiones que ofrece dependen de las fases elegidas. La potencia
se puede indicar en VA o **calcular desde la corriente nominal** (`A × V`), que
es como vienen las fichas técnicas de aires y motores.

```jsonc
{
  "SPLIT-24K": {
    "descripcion": "AIRE SPLIT INVERTER LENNOX 24000 BTU (2 TON)",
    "distancia": 15.0,
    "fase": 2,
    "potencia": 2600,
    "tipo": "aa",
    "tension": 208,
    "conexion": "2PT",
    "eficiencia": 1.0,
    "fp": 1.0,
    "individual": true,
    "continua": true
  }
}
```

Cuando la potencia sale de la corriente de placa, deje `eficiencia` y `fp` en
`1.0`: ese amperaje ya es el consumo real y derratearlo otra vez lo duplicaría.

### `<proyecto>/config_proyecto.jsonc`

Reglas de cálculo propias de un proyecto, junto a `cargas_proyecto.jsonc`. No
es obligatorio: si falta, el cálculo se comporta igual que siempre.

`minimos` fija el calibre y/o el breaker mínimo de una salida, con tres
alcances. Gana la regla más específica, **campo por campo**:

```text
por_codigo  >  por_categoria  >  proyecto
```

```jsonc
{
  "minimos": {
    // Todo el proyecto
    "proyecto": { "calibre": "12 AWG" },

    // Por tipo de carga (IL, TC, AA, ME, EC, EQ, AF)
    "por_categoria": {
      "IL": { "calibre": "12 AWG" },
      "TC": { "calibre": "12 AWG" }
    },

    // Por código de salida concreto
    "por_codigo": {
      "TMC220": { "calibre": "10 AWG" }
    }
  }
}
```

Reglas de aplicación:

- El **breaker se deriva del conductor final**, no de la corriente de la carga:
  NEC 240.4(D) para `#14 → 15 A`, `#12 → 20 A`, `#10 → 30 A`; para calibres
  mayores, el breaker estándar más alto que no supere la ampacidad. Declare
  `breaker_minimo` solo para forzar un valor distinto.
- Un mínimo **nunca reduce** un calibre que el cálculo ya pide mayor.
- Aplica a **cualquier categoría**, incluidas `IL` y `EQ`, que están fuera de
  los mínimos globales de `datos/conexiones_cables.json`.
- Una regla puede abreviarse: `"IL": "12 AWG"` equivale a
  `"IL": {"calibre": "12 AWG"}`, y `"IL": 20` a `{"breaker_minimo": 20}`.
- Los nombres de categoría no distinguen mayúsculas.

`alimentadores` fija el **conductor de un alimentador** en lugar de
dimensionarlo. Sirve cuando el proyecto ya decidió el cable —una revisión que
sube de 1/0 a 2/0, un tramo existente que se reutiliza— y lo que se necesita es
verificarlo, no volver a calcularlo. Misma precedencia, campo por campo:

```text
por_alimentador  >  por_destino  >  proyecto
```

```jsonc
{
  "alimentadores": {
    // Todo el proyecto
    "proyecto": { "factor_dimensionamiento": 1.25 },

    // Por panel/equipo de destino
    "por_destino": { "MPM": { "potencial": "3x2/0" } },

    // Por alimentador concreto: FUENTE>DESTINO:NUMERO
    "por_alimentador": {
      "ITA>MPM:1": {
        "potencial": "3x2/0",
        "neutro": "3x1/0",
        "tierra": "1x2",
        "breaker": 450
      },
      "GEN>ITA:1": "3x2/0"
    }
  }
}
```

Reglas de aplicación:

- La clave es `FUENTE>DESTINO:NUMERO`, con los nombres del CSV de paneles.
  Sin `:NUMERO` la regla cubre todas las casillas de ese par. No distingue
  mayúsculas y el sufijo de casilla que el cálculo agrega (`ITA-1`) no estorba.
- Una regla puede abreviarse: `"GEN>ITA": "3x2/0"` equivale a
  `{"potencial": "3x2/0"}`. El calibre se escribe como en la tabla:
  `"3x2/0"`, `"3x2/0xF"`, `"2x#6"`, `"1x250MCM"`.
- Lo declarado **no se dimensiona**: se usa tal cual, aunque quede sobrado.
  Caída de tensión, tubería y área se calculan sobre ese conductor.
- **Lo que no se declara no cambia.** Fijar las fases no arrastra al neutro, a
  la tierra ni al breaker: el neutro sigue al conductor que el cálculo habría
  dimensionado, la tierra sale del breaker y el breaker sale de la carga y del
  factor de la norma. Para moverlos hay que declararlos.
- `breaker` fija el interruptor y salta su cálculo completo: mínimos de norma,
  límites CEPM y NEC 240.4(D) ya no lo tocan.
- Lo que no cumple sale anotado con asterisco en la tabla de alimentadores y
  desarrollado en la memoria:

  ```text
  *1- F1: Cable no cumple con el 100% de la corriente base. Actualmente solo 65%.
  *2- F1: Cable preseleccionado excede la caída de tensión máxima de 3%: 9.80%.
  *3- F4: Cable preseleccionado no cumple con el 1.25 de dimensionamiento, actualmente es 1.15.
  *4- F5: Breaker preseleccionado de 400A no cubre la corriente de diseño de 431.02A.
  ```

- `factor_dimensionamiento` es el factor contra el que se juzga el cable
  (1.25, 1.15, 1.00 …). Si no se declara se usa el de la norma vigente, el
  mismo de la columna `I(1.25)`. Solo afecta la nota, no la selección de
  breaker.

El archivo se copia junto a `cargas_proyecto.jsonc` al sincronizar o preparar
un proyecto.

### Formato del reporte de transformador

El reporte del transformador tiene dos formatos, y el proyecto elige cuál usar:

| Plantilla | Qué imprime |
|---|---|
| `1` | El formato original, sin la página de selección de potencia |
| `2` | Agrega la página de cálculo de potencia del transformador |

La elección es del proyecto y vale para `01-trf-TRF.html` y para la memoria.
**Mientras la migración no esté cerrada, un proyecto que no elige imprime con
el original**, que es lo que ya tenía entregado: así una revisión nueva de un
proyecto viejo no cambia de formato sin querer.

Se pregunta al crear el proyecto desde el menú, y también se fija sin
interacción:

```bash
orgm-bt project create "Nave BT" --plantilla-trf 2
orgm-bt project edit 'Nave/BT' --plantilla-trf 1
```

En un cálculo suelto con `main.py`, la bandera es `--plantilla-trf {1,2}`.

### Diagrama unifilar

Reporte propio (`diagrama-unifilar.html`), independiente de los demás, que se
dibuja desde el cálculo sin pasar por AutoCAD. El flujo DU sobre plantillas DWG
sigue activo en paralelo.

Al ejecutar el cálculo el menú pregunta **hasta dónde detallar**:

| Nivel | Qué desarrolla |
|---|---|
| `ACOMETIDA` | poste y acometida de media tensión |
| `TRANSFORMADOR` | + banco con kVA y relación |
| `EQUIPOS` | + generador, transferencias, TRFS y totalizadores |
| `MPM` | + módulos de contadores |
| `PANEL` | + panelboards |
| `CIRCUITO` | + cada circuito con su carga |

El nivel es la **última entidad desarrollada**: sus hijos se dibujan como
símbolo terminal, con nombre y carga, y de ahí no se sigue. Un MPM que alimenta
doce apartamentos en nivel `MPM` muestra los doce paneles nombrados, sin abrir
sus circuitos.

```bash
orgm-bt calculate PROYECTO --diagrama-nivel MPM
```

**Espacios disponibles.** La capacidad sale de las series estándar (MPM
12/16/20, panelboard 12/18/24): se toma el menor tamaño que cubra lo conectado.
Se dibujan hasta 3 espacios libres en línea discontinua y el resto se resume
con una marca de continuación.

**Una sola forma de armarlo.** Cada tablero del que salen derivaciones se
dibuja como una barra dentro de un recuadro que abarca todos sus circuitos, con
el nombre y las características fuera del recuadro. El símbolo de panelboard se
reserva para cuando la derivación *termina* en el tablero. Los tableros que
cuelgan van debajo, centrados bajo el que los alimenta.

Cuando la acometida es la red (EDE) no se dibujan poste, pararrayos ni cut-out:
solo el punto de interconexión.

Las variantes por tipo de proyecto —con generador, con transferencia, con celda
de media tensión— serán **plantillas** aparte, no opciones de reparto. Hoy está
la de panel principal y subpaneles.

**Exactitud.** El diagrama se resuelve entero en coordenadas absolutas antes de
dibujar: cada símbolo declara su caja (`tamano()`) y sus puntos de conexión
(`anclas()`), las columnas siguen la jerarquía y los hermanos ocupan bandas
disjuntas. Por eso la ausencia de solapamientos es verificable, y hay una
prueba que la afirma en los seis niveles.

Añadir un elemento nuevo es crear su módulo en `app/diagrama/simbolos/` y
registrarlo en `SIMBOLO_POR_TIPO`.

**Banco de símbolos.** Para afinar un símbolo sin montar un diagrama entero:

```bash
just simbolos              # temp/banco-simbolos.html
```

Dibuja cada símbolo aislado con su caja reservada, su glifo, sus anclas, sus
medidas y los grosores de línea que usa. Una prueba comprueba que todo símbolo
registrado tenga su muestra, así el banco no se queda atrás.

El árbol se exporta además a `diagrama.json`, lo que permite regenerar el
dibujo con otro nivel u otras opciones sin recalcular el proyecto.

## Development

### Project Structure
```
calc/
├── app/
│   ├── utils/              # Utilidades de cálculo
│   │   ├── carga.py        # Conversión y cálculo de cargas
│   │   ├── circuitos.py    # Procesamiento de circuitos
│   │   ├── panel.py        # Gestión de paneles y jerarquías
│   │   ├── mpm.py          # Módulos MPM (Media Power Module)
│   │   ├── trf.py          # Transformadores
│   │   ├── equipos.py      # Equipos especiales (GEN, UPS, etc.)
│   │   ├── cables.py       # Cálculo de cables
│   │   ├── factores_demanda.py  # Factores de demanda
│   │   └── ...             # Otras utilidades
│   ├── services/           # Servicios principales
│   │   ├── generador_pdf.py    # Generación de PDFs
│   │   └── validator.py        # Validación de datos
│   ├── du/                 # Procesamiento de Diagramas Unifilares
│   │   ├── du.py           # Procesador principal de DU
│   │   ├── alimentador.py  # Lógica de alimentadores
│   │   └── dibujante.py    # Generación de diagramas
│   ├── db/                 # Base de datos
│   │   ├── models.py       # Modelos SQLModel
│   │   ├── database.py     # Conexión a base de datos
│   │   └── alembic/        # Migraciones Alembic
│   ├── templates/          # Templates HTML para PDFs
│   │   ├── template-memoria.html
│   │   ├── template-cuadro-distribucion.html
│   │   ├── template-alimentadores.html
│   │   ├── template-mpm.html
│   │   ├── template-trf.html
│   │   ├── template-modulos-mpm-trf.html
│   │   ├── template-equipos.html
│   │   └── template-guia-cargas.html
│   ├── config.py           # Configuración de la aplicación
│   └── __init__.py
├── storage/                # Integración con almacenamiento cloud
│   └── s3.py              # Cliente S3/R2
├── datos/                  # Archivos JSON de configuración
│   ├── cargas_predeterminadas.json    # Códigos de carga
│   ├── motores_predeterminados.json   # Especificaciones de motores
│   ├── categorias.json                # Categorías de cargas
│   ├── factores_demanda.json          # Factores de demanda
│   ├── nema.json                      # Estándares NEMA
│   └── ...                            # Otros archivos de configuración
├── proy/                   # Datos de ejemplo
│   ├── CIRCUITOS/          # Archivos de circuitos
│   ├── PANELES/            # Archivos de paneles
│   └── DU/                 # Archivos de diagramas unifilares
├── temp/                   # Directorio temporal para archivos generados
├── main.py                 # Punto de entrada principal
├── local.py                # Gestor de comandos para uso local
├── api.py                  # API FastAPI (pendiente implementación)
├── alembic.ini             # Configuración de Alembic
├── pyproject.toml          # Dependencias y configuración del proyecto
└── README.md               # Este archivo
```

### Running Tests
```bash
# Test individual services
uv run python scripts/test_calculations.py

# Test with sample data
uv run python scripts/import_data.py --create-project "Test" --import-circuits proy/CIRCUITOS/D\ -\ BT.txt
```

### Database Migrations
```bash
# Create new migration
uv run alembic revision --autogenerate -m "Description"

# Apply migrations
uv run alembic upgrade head
```

## Requirements

- Python 3.11+
- PostgreSQL 12+
- uv (package manager)

### Dependencies
- **PDF Generation**: WeasyPrint, Pillow, ReportLab, svglib
- **Database**: SQLModel, Alembic, psycopg2-binary, pydantic-settings
- **API**: FastAPI, uvicorn (pendiente implementación)
- **Templates**: Jinja2
- **Cloud Storage**: boto3 (S3/R2)
- **Utilities**: rich, tabulate, pandas, python-dotenv
- **Interactive CLI**: questionary

## License

No se ha declarado una licencia de distribución. El titular debe definirla antes
de anunciar el proyecto como software libre; publicar en PyPI no concede por sí
solo permisos de reutilización.

## Publicación en PyPI

Requiere Python 3.11 o posterior. El editor gráfico necesita Tk/Tcl y una sesión
gráfica; el PDF con WeasyPrint necesita sus bibliotecas nativas del sistema
(incluido Pango). Chromium/Chrome es una alternativa externa para PDF.

El paquete incluye catálogos de cálculo, plantillas y CSS/JS. No incluye los
proyectos locales, `.env`, la firma personal ni el carnet CODIA. Cada instalación
debe aportar esos activos a sus perfiles; los archivos locales existentes no
se eliminan al instalar o actualizar.

El token de PyPI se resuelve con `sops-shared-env`; nunca se guarda en el
repositorio. El alias de publicación es:

```bash
alias pypi-publish='sops-shared-env --with UV_PUBLISH_TOKEN -- uv publish dist/*'
```

Como el alias publica `dist/*`, limpie `dist/` antes de construir para que solo
se suban los artefactos de la versión nueva. PyPI rechaza cualquier archivo ya
publicado con el mismo nombre.

El `justfile` automatiza el flujo y limpia `dist/` en cada `build`, de modo que
nunca se intente resubir una versión ya publicada:

```bash
just build      # limpia dist/ y construye solo la versión actual
just check      # build + twine check --strict
just try        # build + instala el wheel como herramienta y lo ejecuta
just publish    # check + sube a PyPI con el token de sops
just release 0.5.7   # bump + lock + test + publish + commit + tag
```

`just release` exige un árbol de git limpio y deja el `git push` en tus manos.
Los pasos equivalentes a mano:

```bash
# 1. Incremente project.version en pyproject.toml y refresque el lock.
uv lock

# 2. Construya solo la versión nueva.
rm -f dist/*.whl dist/*.tar.gz
uv build
uvx twine check --strict dist/orgm_bt-0.5.6.tar.gz dist/orgm_bt-0.5.6-py3-none-any.whl

# 3. Prueba local del paquete, sin instalar desde el código fuente:
uv tool install --from ./dist/orgm_bt-0.5.6-py3-none-any.whl orgm-bt
orgm-bt --help

# 4. Publique.
pypi-publish
```

Antes de publicar, confirme la licencia y la titularidad del nombre `orgm-bt`
en PyPI. Para una nueva entrega, incremente `project.version` en `pyproject.toml`,
ejecute `uv lock` y use los nombres de artefacto de esa versión. PyPI no permite
reemplazar un archivo ya publicado con el mismo nombre.

## Contributing

[Add contribution guidelines here]


## Workspace local

`orgm-bt config setup` prepara los activos y el catálogo central. `orgm-bt init`
solo escribe la guía del agente en el directorio actual.

```text
BT/
├── Assets/
├── Ing/<id>/datos.json, codia.png, firma.png (opcional)
├── Empresas/<id>/datos.json, logo.png
├── Cliente/<id>/datos.json, logo.png
├── Proyectos/<ruta-legible>-<hash>.json
└── BD/*.json
```

`BD/` se inicializa con los catálogos incluidos; después es la única base usada por
los cálculos de ese workspace. Modificar un JSON allí no modifica el programa.

Perfiles de ingeniero, empresa y cliente contienen `datos.json`. Campos admitidos:
`nombre`, `representante`, `correo`, `telefono`, `ubicacion`; el ingeniero añade
`codia`. Los logos locales se copian al resultado HTML, sin descargarlos de red.

Cada JSON de `BT/Proyectos/` persiste la ruta relativa a `projects_dir`.
CSV, circuitos y resultados permanecen en esa carpeta de cálculo; el CLI no usa
su `datos.json`, que puede pertenecer a una integración de API. Editar o borrar
un proyecto afecta al catálogo central, no a sus archivos de cálculo.

```json
{
  "nombre": "Nave de los Chinos",
  "ruta": "608 - NAVE DE LOS CHINOS/Calculo/ELEC",
  "ingeniero": "osmar",
  "empresa": "",
  "cliente": "cliente-bohc-projects-srl",
  "calculo_realizado": "Memoria de cálculo BT",
  "revision": 1,
  "descripcion_revision": "Emisión inicial",
  "ubicacion": "Santo Domingo",
  "norma": "MOPC"
}
```

### Flujo terminal

```bash
# Crear perfiles; copie después logo.png, codia.png o firma.png a sus carpetas.
orgm-bt profile create engineer osmar --name "Ing. Osmar Garcia" --codia 36467
orgm-bt profile create client cliente-bohc-projects-srl --name "BOHC PROJECTS SRL"

# Crear proyecto y la plantilla PANELES/PANELES.csv.
orgm-bt project create "Nave de los Chinos" \
  --relative-path "608 - NAVE DE LOS CHINOS/Calculo/ELEC" \
  --engineer osmar --client cliente-bohc-projects-srl

# La lista nunca se imprime al iniciar. Solicítela explícitamente.
orgm-bt project list --limit all
orgm-bt project list --limit 10
orgm-bt project list --search chinos --limit all

# Generar HTML, editar paneles con Tkinter y gestionar resultados.
orgm-bt calculate "Nave de los Chinos" --revision-description "Emisión inicial"
orgm-bt panels "Nave de los Chinos"
orgm-bt du "Nave de los Chinos" 1
orgm-bt finalize "Nave de los Chinos" 1
orgm-bt pdf "Nave de los Chinos" 1 --renderer chromium
```

Sin subcomando, `orgm-bt` y `uv run local.py` muestran un menú corto: calcular,
ver proyectos (todos, cantidad o búsqueda), crear o editar paneles. No imprime una
tabla de proyectos antes de que el usuario la pida. Los switches legacy de
`local.py` siguen disponibles para instalaciones que aún usan `datos/local.json`.

## Pendiente

- **Diagrama Unifilar**: Procesamiento básico implementado (archivo DU), pendiente visualización completa y generación automática del diagrama
- **Feed Lugs**: Alimentación a través de terminal (no va en un breaker). Pendiente implementar en cálculos y documentos.
- **FastAPI simple de uso interno**: Implementar API REST simple para uso interno del equipo, permitiendo:
  - Crear y gestionar proyectos
  - Ejecutar cálculos via API
  - Obtener resultados y documentos generados
  - Subir archivos de entrada
- **Actualizar Alembic con datos de local.json**: Crear migraciones de Alembic para almacenar en base de datos todos los campos de configuración que actualmente se guardan en `local.json`, excepto:
  - `path`: Ruta local (no se almacena en BD)
  - `output`: Ruta local de salida (no se almacena en BD)
  - `fecha_creacion` y `fecha_calculo`: Se pueden usar timestamps de BD

Esto permitirá centralizar la gestión de proyectos y configuraciones en la base de datos en lugar de archivos JSON locales.
