# Central Edictos

Sistema automatizado de descarga, extracción y procesamiento de edictos societarios publicados en los Boletines Oficiales de las provincias argentinas.

## Flujo de trabajo

```mermaid
flowchart TD
    CRON["Cron diario (L-V)"]
    ORCH["procesamiento/descarga_pdf/\ndescarga_boletines_pdf.py\norquestador"]
    SCRIPTS["procesamiento/descarga_pdf/scripts_descarga_boletines/\ndescargar_{provincia}.py"]
    PDF["procesamiento/descarga_pdf/boletines/{provincia}/sin_procesar/\n*.pdf"]

    CRON --> ORCH
    ORCH -->|"schedule.yaml"| SCRIPTS
    SCRIPTS -->|"descarga PDFs"| PDF

    PDF --> VERTEX["analisis_ai/procesar_boletin_vertex.py\nVertex AI (Service Account)"]
    PDF --> GEMINI["analisis_ai/procesar_boletin_gemini.py\nGemini API Key"]
    VERTEX -->|"Pass 1: detecta edictos\nPass 2: extrae texto completo"| JSON["boletines_json/\nsociedades_{stem}.json"]
    GEMINI -->|"Pass 1: detecta edictos\nPass 2: extrae texto completo"| JSON

    JSON --> PROCJSON["analisis_ai/procesar_json_edictos.py\nbusca CUIT en padron"]
    PROCJSON -->|"SP insert_edictos_queue_staging"| DB[("SQL Server\nFIDELITAS")]

    DB --> GENBATCH["batch_openai/generar_batch.py\nempaqueta edictos en JSONL"]
    GENBATCH -->|"sube archivo"| OPENAI["OpenAI Batch API\n(24h window)"]
    OPENAI -->|"batch_openai/consultar_batch.py\npolling"| OUTPUT["batch_output/\n*.jsonl"]
    OUTPUT --> PROCOUT["batch_openai/procesar_output.py\nparsea respuestas"]
    PROCOUT -->|"SPs openai_insert_*"| DB

    DB --> HOMO["homonimia/limpieza_homonimia.py\nresuelve CUITs ambiguos"]
    DB --> SOCIOS["socios_accionistas/ejecutar_todos.py\nactualiza personas y vinculaciones"]

    PDF -->|"OK -> procesado/\nERROR -> error/"| PDF
```

## Estructura del proyecto

```
central_edictos/
├── schedule.yaml                        # Configuracion de provincias y dias de descarga
├── requirements.txt
├── .env                                 # Variables de entorno (ver .env.example)
├── service-account.json                 # Credenciales de Vertex AI
│
├── files/
│   ├── boletines/                       # PDFs organizados por provincia
│   │   └── {provincia}/
│   │       ├── sin_procesar/
│   │       ├── procesado/
│   │       └── error/
│   └── boletines_json/                  # JSONs extraidos por los modelos de IA
│       └── {provincia}/
│           ├── *.json
│           ├── procesados/
│           ├── sin_edictos/
│           └── error/
│
├── procesamiento/
│   ├── descarga_pdf/                    # Descarga de boletines
│   │   ├── descarga_boletines_pdf.py    # Orquestador principal (lee schedule.yaml)
│   │   └── scripts_descarga_boletines/  # Un script por provincia
│   │       ├── descargar_bora.py
│   │       ├── descargar_ba.py
│   │       └── ...
│   │
│   ├── analisis_ai/                     # Extraccion de edictos con IA
│   │   ├── procesar_boletin_vertex.py   # Extraccion via Vertex AI (Service Account)
│   │   ├── procesar_boletin_gemini.py   # Extraccion via Gemini API
│   │   └── procesar_json_edictos.py     # Inserta JSON extraido en la DB
│   │
│   ├── batch_openai/                    # Procesamiento batch con OpenAI
│   │   ├── generar_batch.py             # Crea batches de OpenAI
│   │   ├── consultar_batch.py           # Polling de estado de batches
│   │   ├── procesar_output.py           # Parsea respuestas y guarda en DB
│   │   └── openai_client.py             # Wrapper de la API de OpenAI
│   │
│   ├── homonimia/
│   │   └── limpieza_homonimia.py        # Resolucion de CUITs ambiguos por homonimia
│   │
│   └── socios_accionistas/              # Actualizacion de personas y vinculaciones
│       ├── ejecutar_todos.py            # Ejecuta todos los scripts en orden
│       ├── update_sociedades_constituciones.py
│       ├── update_personas.py
│       ├── update_personas_socios.py
│       ├── update_personas_no_socios.py
│       ├── update_vinculaciones.py
│       └── update_vinculadas_socios.py
│
├── helpers/
│   ├── db.py                            # Conexion SQL Server y ejecucion de SPs
│   ├── buscar_cuit.py                   # Lookup de CUIT en padron
│   ├── cuit_homonimia.py               # Lookup de CUIT con manejo de homonimia
│   ├── logging_setup.py                 # Configuracion centralizada de logs
│   ├── provincias.py                    # Datos de provincias
│   └── singleton.py                     # Guard de instancia unica por proceso
│
└── logs/                                # Logs de ejecucion por modulo
```

## Instalacion

```bash
pip install -r requirements.txt
```

### Dependencias del sistema

- Python 3.8+
- [ODBC Driver 18 para SQL Server](https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server)

### Variables de entorno

Copia `.env.example` a `.env` y completa los valores:

```bash
cp .env.example .env
```

| Variable | Descripcion |
|---|---|
| `DB_SERVER` | IP del servidor SQL Server |
| `DB_DATABASE` | Nombre de la base de datos |
| `DB_USER` | Usuario SQL |
| `DB_PASSWORD` | Contrasena SQL |
| `DB_DRIVER` | Driver ODBC (ej: `ODBC Driver 18 for SQL Server`) |
| `GEMINI_API_KEY` | API key de Google Gemini |
| `GOOGLE_CLOUD_PROJECT_ID` | ID del proyecto en Google Cloud (Vertex AI) |
| `APIKEY_CHATGPT` | API key de OpenAI |
| `CHAT_KEY` | API key alternativa de OpenAI |
| `PATH_BATCH_INPUT` | Carpeta de entrada para batches OpenAI |
| `PATH_BATCH_OUTPUT` | Carpeta de salida de batches OpenAI |
| `BATCH_PATH_OUTPUT_PROCESADO` | Carpeta de batches ya procesados |

Para Vertex AI se requiere ademas el archivo `service-account.json` con las credenciales de la service account de Google Cloud en la raiz del proyecto.

## Uso

### Descarga de boletines

Ejecutar el orquestador (detecta el dia de la semana y corre los scripts correspondientes segun `schedule.yaml`):

```bash
python3 procesamiento/descarga_pdf/descarga_boletines_pdf.py
```

Ejecutar un script de provincia individual:

```bash
python3 procesamiento/descarga_pdf/scripts_descarga_boletines/descargar_cordoba.py --directorio ./boletines/cordoba/sin_procesar
python3 procesamiento/descarga_pdf/scripts_descarga_boletines/descargar_sanjuan.py --anio 2025 --mes 4
```

### Extraccion de edictos societarios

Hay dos extractores disponibles. Ambos realizan dos pasadas sobre el PDF:
1. **Pass 1** — detecta todos los edictos societarios (constituciones, cambios de directorio, disoluciones, etc.) y la fecha de publicacion.
2. **Pass 2** — extrae el texto completo de cada edicto detectado en batches de 25.

El resultado se guarda en `boletines_json/` como JSON estructurado.

**Via Vertex AI** (usa `service-account.json`, con fallback entre modelos):

```bash
python3 procesamiento/analisis_ai/procesar_boletin_vertex.py boletin.pdf
python3 procesamiento/analisis_ai/procesar_boletin_vertex.py boletin.pdf --model gemini-2.5-flash
python3 procesamiento/analisis_ai/procesar_boletin_vertex.py boletin.pdf --estimate
python3 procesamiento/analisis_ai/procesar_boletin_vertex.py boletin.pdf --out directorio_salida
```

**Via Gemini API** (usa `GEMINI_API_KEY` del `.env`):

```bash
python3 procesamiento/analisis_ai/procesar_boletin_gemini.py boletin.pdf
python3 procesamiento/analisis_ai/procesar_boletin_gemini.py boletines/bora/sin_procesar/
python3 procesamiento/analisis_ai/procesar_boletin_gemini.py boletin.pdf --estimate
python3 procesamiento/analisis_ai/procesar_boletin_gemini.py boletin.pdf --model gemini-2.5-flash
```

### Insercion en base de datos

```bash
python3 procesamiento/analisis_ai/procesar_json_edictos.py
```

### Procesamiento batch con OpenAI

```bash
# 1. Generar batch (por defecto 100 edictos)
python3 procesamiento/batch_openai/generar_batch.py 100

# 2. Consultar estado y descargar resultados
python3 procesamiento/batch_openai/consultar_batch.py

# 3. Procesar output e insertar en DB
python3 procesamiento/batch_openai/procesar_output.py
```

### Limpieza de homonimia

Resuelve edictos donde el CUIT no pudo determinarse automaticamente por homonimia:

```bash
python3 procesamiento/homonimia/limpieza_homonimia.py
```

### Actualizacion de socios y accionistas

```bash
python3 procesamiento/socios_accionistas/ejecutar_todos.py
```

## Cobertura provincial

| Provincia | Script | Dias |
|---|---|---|
| BORA (Nacional) | `descargar_bora.py` | L-V |
| Buenos Aires | `descargar_ba.py` | L-V |
| Catamarca | `descargar_catamarca.py` | Mar, Vie |
| Chaco | `descargar_chaco.py` | Lun, Mie, Vie |
| Chubut | `descargar_chubut.py` | L-V |
| Cordoba | `descargar_cordoba.py` | L-V |
| Corrientes | `descargar_corrientes.py` | L-V |
| Entre Rios | `descargar_entrerios.py` | L-V |
| Jujuy | `descargar_jujuy.py` | Lun, Mie, Vie |
| La Pampa | `descargar_lapampa.py` | Vie |
| La Rioja | `descargar_larioja.py` | Mar, Vie |
| Mendoza | `descargar_mendoza.py` | L-V |
| Misiones | `descargar_misiones.py` | L-V |
| Neuquen | `descargar_neuquen.py` | Mar, Vie |
| Rio Negro | `descargar_rionegro.py` | Lun, Jue |
| Salta | `descargar_salta.py` | L-V |
| San Juan | `descargar_sanjuan.py` | L-V |
| San Luis | `descargar_sanluis.py` | Lun, Mie, Vie |
| Santa Cruz | `descargar_santacruz.py` | Mar, Jue |
| Santa Fe | `descargar_santafe.py` | L-V |
| Santiago del Estero | `descargar_sde.py` | L-V (inactivo) |
| Tierra del Fuego | `descargar_tierrafuego.py` | L-V (inactivo) |
| Tucuman | `descargar_tucuman.py` | L-V |

Provincias sin script aun: **CABA**, **Formosa**

## Tipos de edictos extraidos

- **constitucion** — creacion de nueva S.A., S.R.L. u otro tipo societario
- **cambio_directorio** — designacion, renovacion o remocion de autoridades
- **disolucion** — liquidacion voluntaria de la sociedad
- **cesion_cuotas** — transferencia de participacion en S.R.L.
- **llamado_asamblea** — convocatoria a asamblea ordinaria o extraordinaria
- **aumento_capital** — incremento del capital social

## Cron sugerido

```cron
# Descarga diaria L-V a las 8am
0 8 * * 1-5 python3 /home/fidelitas/Code/central_edictos/procesamiento/descarga_pdf/descarga_boletines_pdf.py

# Extraccion Vertex AI a las 10pm
30 22 * * 1-5 python3 /home/fidelitas/Code/central_edictos/procesamiento/analisis_ai/procesar_boletin_vertex.py /home/fidelitas/Code/central_edictos/procesamiento/descarga_pdf/boletines

# Insertar JSON en DB
0 23 * * 1-5 python3 /home/fidelitas/Code/central_edictos/procesamiento/analisis_ai/procesar_json_edictos.py

# Generar batch OpenAI al mediodia
0 12 * * * python3 /home/fidelitas/Code/central_edictos/procesamiento/batch_openai/generar_batch.py 100

# Polling de batches cada 30 min
*/30 * * * * python3 /home/fidelitas/Code/central_edictos/procesamiento/batch_openai/consultar_batch.py

# Procesar output cada 10 min
*/10 * * * * python3 /home/fidelitas/Code/central_edictos/procesamiento/batch_openai/procesar_output.py
```
