qry-doc 0.1.51
Motor de análisis generativo que transforma el lenguaje natural en código ejecutable, visualizaciones y reportes PDF profesionales.
Requires Python: >=3.11

<p align="center">
  <img src="https://raw.githubusercontent.com/danro-dev/qry-proyect-examples/refs/heads/master/public/logo_op.png" alt="qry-doc" width="600" heigt="200">
</p>

<h1 align="center">qry-doc</h1>


<p align="center">
  <strong>Motor de análisis generativo para consultas en lenguaje natural</strong>
</p>

  <a href="#instalación">Instalación</a> 
  
  <a href="#inicio-rápido">Inicio Rápido</a>

  <a href="#características">Características</a>

  <a href="#ejemplos">Ejemplos</a> 

  <a href="#api">API</a> 

  <a href="#contribuir">Contribuir</a>


---

**qry-doc** transforma el lenguaje natural en código ejecutable, visualizaciones y reportes PDF profesionales. Simplifica radicalmente la interacción con archivos CSV y bases de datos SQL.

```python
from qry_doc import QryDoc, ReportTemplate
import pandasai as pai
from pandasai_openai import OpenAI

# Configurar LLM
llm = OpenAI()
pai.config.set({"llm": llm})

qry = QryDoc("ventas.csv", llm=llm)

# Pregunta en español
respuesta = qry.ask("¿Cuál fue el producto más vendido en 2024?")
print(respuesta)  # "El producto más vendido fue 'Laptop Pro' con 1,234 unidades"

# Exporta resultados a CSV
qry.extract_to_csv("Top 10 clientes por ingresos", "top_clientes.csv")

# Genera reporte PDF profesional
qry.generate_report("Análisis trimestral de ventas", "reporte_q4.pdf")
```

## Características

- 🗣️ **Consultas en lenguaje natural** - Pregunta sobre tus datos como si hablaras con un analista
- 📊 **Visualizaciones automáticas** - Genera gráficos relevantes sin escribir código
- 📄 **Reportes PDF profesionales** - Crea documentos con tablas, gráficos y resúmenes
- 📁 **Exportación CSV** - Extrae resultados tabulares con encoding Excel-compatible
- 🎨 **Templates personalizables** - Adapta el estilo de reportes a tu marca
- 🖼️ **Portadas personalizadas** - Añade imágenes de portada a tus reportes
- 🏷️ **Logo en pie de página** - Logo por defecto o personalizado con posición configurable
- ✏️ **Fuentes personalizadas** - Soporte para fuentes TTF/OTF
- 📑 **Sistema de secciones** - Controla el orden y contenido de las secciones del reporte
- 🔒 **Sanitización de errores** - Protege información sensible en mensajes de error
- 🐍 **Python 3.11+** - Type hints completos y código moderno

### Nuevas funcionalidades v0.1.5

- 🤖 **AIBuilder** - Agente inteligente con LangChain para preparación de datos y sugerencias de gráficas
- 🎯 **CoverBuilder** - API fluida para crear portadas dinámicas con posicionamiento preciso
- 🏗️ **TemplateBuilder** - Constructor fluido para templates personalizados
- 📊 **Multi-gráficas** - Soporte para hasta 10 gráficas por reporte con ChartConfig
- 🏭 **ReportPresets** - Presets predefinidos por industria (Financial, Healthcare, Technology, etc.)

## Instalación

### Con uv (recomendado)

```bash
# Instalación básica
uv add qry-doc

# Con soporte para OpenAI
uv add "qry-doc[openai]"

# Con soporte para múltiples proveedores
uv add "qry-doc[litellm]"

# Con soporte para AIBuilder (LangChain)
uv add "qry-doc[langchain]"

# Con todos los proveedores
uv add "qry-doc[all]"
```

### Con pip

```bash
# Instalación básica
pip install qry-doc

# Con soporte para OpenAI
pip install "qry-doc[openai]"

# Con soporte para múltiples proveedores
pip install "qry-doc[litellm]"

# Con soporte para AIBuilder (LangChain)
pip install "qry-doc[langchain]"

# Con todos los proveedores
pip install "qry-doc[all]"
```

### Desde código fuente

```bash
git clone https://github.com/danro-dev/qry-doc.git
cd qry-doc
uv sync
```

## Inicio Rápido

### 1. Configura tu LLM

qry-doc utiliza [PandasAI](https://pandas-ai.com/) como motor de IA. Instala con el extra que necesites:

```bash
# Para OpenAI
pip install "qry-doc[openai]"

# Para múltiples proveedores
pip install "qry-doc[litellm]"
```

```python
import pandasai as pai
from pandasai_openai import OpenAI

# Opción 1: API key como variable de entorno OPENAI_API_KEY
llm = OpenAI()

# Opción 2: Pasar el API key directamente
llm = OpenAI(api_token="sk-...")

# Configurar PandasAI
pai.config.set({"llm": llm})
```

**Proveedores soportados vía LiteLLM:**
- OpenAI (GPT-4, GPT-3.5)
- Anthropic (Claude)
- Google (Gemini)
- Azure OpenAI
- Ollama (modelos locales)
- Y muchos más...

### 2. Carga tus datos

```python
from qry_doc import QryDoc
import pandas as pd

# Desde archivo CSV
qry = QryDoc("datos.csv", llm=llm)

# Desde DataFrame
df = pd.read_excel("datos.xlsx")
qry = QryDoc(df, llm=llm)

# Desde SQL (próximamente)
# qry = QryDoc("postgresql://user:pass@host/db", llm=llm)
```

### 3. Haz preguntas

```python
# Preguntas simples
total = qry.ask("¿Cuál es el total de ventas?")

# Análisis complejos
tendencia = qry.ask("¿Cómo han evolucionado las ventas mes a mes en 2024?")

# Comparaciones
comparacion = qry.ask("Compara las ventas de Q1 vs Q2 por región")
```

## Ejemplos

### Exportar datos a CSV

```python
from qry_doc import QryDoc
import pandasai as pai
from pandasai_openai import OpenAI

llm = OpenAI()
pai.config.set({"llm": llm})
qry = QryDoc("clientes.csv", llm=llm)

# Extrae datos filtrados
qry.extract_to_csv(
    "Clientes con más de 10 compras en el último año",
    "clientes_frecuentes.csv"
)

# Extrae agregaciones
qry.extract_to_csv(
    "Ventas totales por categoría de producto",
    "ventas_por_categoria.csv"
)

# Incluir índice si es necesario
qry.extract_to_csv(
    "Ranking de vendedores por comisión",
    "ranking.csv",
    include_index=True
)
```

### Generar reportes PDF

```python
from qry_doc import QryDoc, ReportTemplate
import pandasai as pai
from pandasai_openai import OpenAI

llm = OpenAI()
pai.config.set({"llm": llm})
qry = QryDoc("ventas_2024.csv", llm=llm)

# Reporte con template por defecto
qry.generate_report(
    "Análisis de rendimiento del equipo de ventas",
    "reporte_ventas.pdf"
)

# Reporte con template personalizado
template = ReportTemplate(
    logo_path="mi_logo.png",
    primary_color="#003366",
    title_font="Helvetica-Bold",
    body_font="Helvetica"
)

qry.generate_report(
    "Resumen ejecutivo Q4 2024",
    "resumen_q4.pdf",
    title="Informe Trimestral de Ventas",
    template=template
)
```

### Reportes con portada personalizada

```python
from qry_doc import ReportTemplate
from pathlib import Path

# Template con imagen de portada
template = ReportTemplate(
    cover_image_path=Path("mi_portada.png"),  # Imagen a página completa
    logo_path="mi_logo.png",
    primary_color="#003366"
)

qry.generate_report(
    "Análisis anual de ventas",
    "reporte_anual.pdf",
    title="Informe Anual 2024",
    template=template
)
```

### Personalización del logo en pie de página

```python
from qry_doc import ReportTemplate, LogoPosition

# Logo personalizado en posición específica
template = ReportTemplate(
    footer_logo_path=Path("mi_marca.png"),     # Logo personalizado (None = logo por defecto)
    footer_logo_enabled=True,                   # Activar/desactivar logo
    footer_logo_position=LogoPosition.BOTTOM_LEFT,  # BOTTOM_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER
    footer_logo_width=50.0,                     # Ancho en puntos
    footer_logo_height=25.0                     # Alto en puntos
)

# Desactivar el logo del pie de página
template_sin_logo = ReportTemplate(
    footer_logo_enabled=False
)
```

### Fuentes personalizadas (TTF/OTF)

```python
from qry_doc import ReportTemplate
from pathlib import Path

# Usar fuentes personalizadas
template = ReportTemplate(
    custom_title_font_path=Path("fonts/Montserrat-Bold.ttf"),
    custom_body_font_path=Path("fonts/OpenSans-Regular.ttf"),
    primary_color="#1a1a2e"
)

# Si la fuente no existe o es inválida, se usa Helvetica automáticamente
```

### Sistema de secciones personalizadas

```python
from qry_doc import ReportTemplate, SectionType, SectionConfig

# Definir orden y contenido de secciones
sections = [
    SectionConfig(SectionType.COVER),           # Portada
    SectionConfig(SectionType.SUMMARY),         # Resumen ejecutivo
    SectionConfig(SectionType.CHART),           # Gráficos
    SectionConfig(SectionType.DATA),            # Tabla de datos
    SectionConfig(                              # Sección personalizada
        SectionType.CUSTOM,
        custom_content="Este es contenido adicional personalizado."
    ),
]

template = ReportTemplate(
    cover_image_path=Path("portada.png"),
    sections=sections
)

# Desactivar una sección específica
sections_sin_datos = [
    SectionConfig(SectionType.SUMMARY),
    SectionConfig(SectionType.CHART),
    SectionConfig(SectionType.DATA, enabled=False),  # No mostrar tabla
]
```

### Templates predefinidos

```python
from qry_doc import (
    DEFAULT_TEMPLATE,    # Estilo profesional estándar
    CORPORATE_TEMPLATE,  # Azul corporativo
    MINIMAL_TEMPLATE,    # Minimalista
    A4_TEMPLATE,         # Tamaño A4 (europeo)
)

qry.generate_report(
    "Análisis de mercado",
    "analisis.pdf",
    template=CORPORATE_TEMPLATE
)
```

### CoverBuilder - Portadas dinámicas (v0.1.5)

```python
from qry_doc import QryDoc, CoverBuilder, TextAlignment
from datetime import datetime

qry = QryDoc("ventas.csv", llm=llm)

# Crear portada con API fluida
cover = (
    qry.create_cover()
    .set_title("Reporte Anual 2024", font_size=48, color="#003366")
    .set_subtitle("Análisis de Ventas", font_size=24)
    .set_date(datetime.now())
    .set_author("Equipo de Datos")
    .set_background_color("#F5F5F5")
)

# Generar reporte con portada personalizada
qry.generate_report_with_builder(
    "reporte_anual.pdf",
    cover=cover,
    title="Informe Anual"
)
```

### TemplateBuilder - Templates personalizados (v0.1.5)

```python
from qry_doc import QryDoc, TemplateBuilder, SectionConfig, SectionType, LogoPosition

qry = QryDoc("ventas.csv", llm=llm)

# Crear template con API fluida
template = (
    qry.create_template()
    .with_colors(primary="#003366", secondary="#0066CC")
    .with_fonts(title_font="Helvetica-Bold", body_font="Helvetica")
    .with_margins(top=72, bottom=72, left=72, right=72)
    .with_header(logo_path="mi_logo.png", height=50)
    .with_footer(logo_position=LogoPosition.BOTTOM_RIGHT)
    .with_sections([
        SectionConfig(SectionType.COVER),
        SectionConfig(SectionType.SUMMARY),
        SectionConfig(SectionType.CHART),
        SectionConfig(SectionType.DATA),
    ])
)

qry.generate_report_with_builder(
    "reporte.pdf",
    template=template,
    title="Mi Reporte"
)
```

### ReportPresets - Presets por industria (v0.1.5)

```python
from qry_doc import TemplateBuilder, ReportPresetType, ReportPreset

# Usar preset predefinido
template = TemplateBuilder.from_preset(ReportPresetType.FINANCIAL).build()

# Presets disponibles:
# - FINANCIAL: Azul corporativo para banca e inversiones
# - HEALTHCARE: Verde/teal para salud y farmacéutica
# - TECHNOLOGY: Púrpura moderno para software y TI
# - RETAIL: Naranja vibrante para comercio y ventas
# - MANUFACTURING: Gris industrial para producción
# - CONSULTING: Azul marino/dorado para consultoría

# Listar todos los presets
for name, description in ReportPreset.list_all():
    print(f"{name}: {description}")

# Personalizar un preset
template = (
    TemplateBuilder.from_preset(ReportPresetType.TECHNOLOGY)
    .with_margins(top=100)  # Override solo el margen superior
    .build()
)
```

### Multi-gráficas con ChartConfig (v0.1.5)

```python
from qry_doc import QryDoc, TemplateBuilder, ChartConfig

qry = QryDoc("ventas.csv", llm=llm)

# Definir múltiples gráficas (máximo 10)
charts = [
    ChartConfig(
        chart_type='bar',
        title='Ventas por Región',
        group_by='region',
        value_column='total',
        color='#003366'
    ),
    ChartConfig(
        chart_type='pie',
        title='Distribución por Categoría',
        group_by='categoria',
        value_column='cantidad'
    ),
    ChartConfig(
        chart_type='line',
        title='Tendencia Mensual',
        group_by='mes',
        value_column='ventas'
    ),
]

# Crear template con múltiples gráficas
template = (
    qry.create_template()
    .with_colors("#003366")
    .with_charts(charts)
)

qry.generate_report_with_builder(
    "reporte_completo.pdf",
    template=template,
    title="Análisis Completo de Ventas"
)
```

### AIBuilder - Preparación inteligente de datos (v0.1.5)

```python
from qry_doc import QryDoc

qry = QryDoc("ventas.csv", llm=llm)

# Obtener el AIBuilder
ai = qry.ai_builder

# Obtener resumen estructurado de los datos
summary = ai.get_data_summary()
print(f"Columnas: {summary.columns}")
print(f"Columnas numéricas: {summary.numeric_columns}")
print(f"Columnas categóricas: {summary.categorical_columns}")

# Obtener sugerencias de gráficas basadas en los datos
suggestions = ai.suggest_charts("Quiero analizar tendencias de ventas")
for suggestion in suggestions:
    print(f"Tipo: {suggestion.config.chart_type}")
    print(f"Razón: {suggestion.reasoning}")
    print(f"Confianza: {suggestion.confidence}")

# Preparar datos estructurados para un reporte
report_data = ai.prepare_report_data("Crear reporte trimestral de ventas")
print(f"Título sugerido: {report_data['title']}")
print(f"Puntos clave: {report_data['summary_points']}")
print(f"Gráficas sugeridas: {len(report_data['charts'])}")

# Validar si una consulta es factible
is_valid, error = ai.validate_query("Total de ventas por región")
if not is_valid:
    print(f"Consulta inválida: {error}")

# Preguntar sobre los datos (requiere LangChain)
if ai.has_langchain:
    response = ai.ask("¿Cuáles son las columnas disponibles?")
    print(response)
```

### Uso como context manager

```python
from qry_doc import QryDoc
import pandasai as pai
from pandasai_openai import OpenAI

llm = OpenAI()
pai.config.set({"llm": llm})

# Limpieza automática de archivos temporales
with QryDoc("datos.csv", llm=llm) as qry:
    respuesta = qry.ask("¿Cuántos registros hay?")
    qry.generate_report("Resumen de datos", "resumen.pdf")
# Los archivos temporales se eliminan automáticamente
```

### Acceso a datos subyacentes

```python
import pandasai as pai
from pandasai_openai import OpenAI

llm = OpenAI()
pai.config.set({"llm": llm})
qry = QryDoc("datos.csv", llm=llm)

# Obtener el DataFrame
df = qry.dataframe

# Ver columnas disponibles
print(qry.columns)  # ['fecha', 'producto', 'cantidad', 'precio']

# Ver dimensiones
print(qry.shape)  # (1000, 4)
```

## API

### QryDoc

Clase principal que actúa como punto de entrada (Facade) para toda la funcionalidad.

```python
QryDoc(
    data_source: str | Path | DataFrame,  # Fuente de datos
    llm: Any,                              # Proveedor LLM
    api_key: str | None = None             # API key opcional
)
```

**Métodos:**

| Método | Descripción | Retorno |
|--------|-------------|---------|
| `ask(query)` | Pregunta en lenguaje natural | `str` |
| `extract_to_csv(query, path, include_index=False)` | Exporta resultado a CSV | `str` |
| `generate_report(query, path, title, template)` | Genera reporte PDF | `str` |
| `generate_report_with_builder(path, cover, template, title)` | Genera reporte con builders (v0.1.5) | `str` |
| `create_cover()` | Crea un CoverBuilder (v0.1.5) | `CoverBuilder` |
| `create_template()` | Crea un TemplateBuilder (v0.1.5) | `TemplateBuilder` |

**Propiedades:**

| Propiedad | Descripción | Tipo |
|-----------|-------------|------|
| `dataframe` | DataFrame subyacente | `pd.DataFrame` |
| `columns` | Nombres de columnas | `list[str]` |
| `shape` | Dimensiones (filas, columnas) | `tuple[int, int]` |
| `ai_builder` | AIBuilder configurado (v0.1.5) | `AIBuilder` |

### ReportTemplate

Configuración para personalizar el estilo de reportes PDF.

```python
ReportTemplate(
    # Configuración básica
    logo_path: Path | None = None,      # Ruta al logo del header
    primary_color: str = "#1a1a2e",     # Color principal (hex)
    title_font: str = "Helvetica-Bold", # Fuente de títulos
    body_font: str = "Helvetica",       # Fuente de cuerpo
    page_size: tuple = letter,          # Tamaño de página
    margin_top: float = 72.0,           # Margen superior (puntos)
    margin_bottom: float = 72.0,        # Margen inferior
    margin_left: float = 72.0,          # Margen izquierdo
    margin_right: float = 72.0,         # Margen derecho
    
    # Portada (v0.2.3+)
    cover_image_path: Path | None = None,  # Imagen de portada a página completa
    
    # Logo en pie de página (v0.2.3+)
    footer_logo_path: Path | None = None,  # Logo personalizado (None = default)
    footer_logo_enabled: bool = True,      # Activar/desactivar logo
    footer_logo_position: LogoPosition = LogoPosition.BOTTOM_RIGHT,
    footer_logo_width: float = 40.0,       # Ancho en puntos
    footer_logo_height: float = 20.0,      # Alto en puntos
    
    # Fuentes personalizadas (v0.2.3+)
    custom_title_font_path: Path | None = None,  # Fuente TTF/OTF para títulos
    custom_body_font_path: Path | None = None,   # Fuente TTF/OTF para cuerpo
    
    # Secciones (v0.2.3+)
    sections: list[SectionConfig] = [],    # Lista de secciones del reporte
)
```

### SectionType (v0.2.3+)

Enum para tipos de secciones disponibles en un reporte.

| Valor | Descripción |
|-------|-------------|
| `COVER` | Portada con imagen a página completa |
| `SUMMARY` | Sección de resumen ejecutivo |
| `DATA` | DataFrame renderizado como tabla |
| `CHART` | Imagen de gráfico/visualización |
| `CUSTOM` | Contenido personalizado arbitrario |

### SectionConfig (v0.2.3+)

Configuración para una sección del reporte.

```python
SectionConfig(
    section_type: SectionType,           # Tipo de sección
    enabled: bool = True,                # Si la sección está activa
    custom_content: str | None = None,   # Contenido para secciones CUSTOM
)
```

### LogoPosition (v0.2.3+)

Enum para posiciones del logo en el pie de página.

| Valor | Descripción |
|-------|-------------|
| `BOTTOM_RIGHT` | Esquina inferior derecha (default) |
| `BOTTOM_LEFT` | Esquina inferior izquierda |
| `BOTTOM_CENTER` | Centro inferior |

### AssetManager (v0.2.3+)

Clase para gestión de assets del paquete (uso avanzado).

```python
from qry_doc import AssetManager

# Obtener ruta al logo por defecto del paquete
default_logo = AssetManager.get_default_logo_path()

# Validar rutas de imágenes
is_valid, error = AssetManager.validate_image_path(Path("imagen.png"))

# Validar rutas de fuentes
is_valid, error = AssetManager.validate_font_path(Path("fuente.ttf"))
```

### CoverBuilder (v0.1.5)

Constructor fluido para portadas dinámicas.

```python
CoverBuilder(page_size: tuple = letter)
```

**Métodos:**

| Método | Descripción | Retorno |
|--------|-------------|---------|
| `set_title(text, font_size, x, y, color, font_family, alignment)` | Configura el título | `CoverBuilder` |
| `set_subtitle(text, ...)` | Configura el subtítulo | `CoverBuilder` |
| `set_date(date, format, ...)` | Configura la fecha | `CoverBuilder` |
| `set_author(name, ...)` | Configura el autor | `CoverBuilder` |
| `add_custom_text(text, x, y, ...)` | Añade texto personalizado | `CoverBuilder` |
| `set_background_image(path, opacity)` | Configura imagen de fondo | `CoverBuilder` |
| `set_background_color(color)` | Configura color de fondo | `CoverBuilder` |
| `build()` | Construye CoverConfig | `CoverConfig` |

### TemplateBuilder (v0.1.5)

Constructor fluido para templates de reportes.

```python
TemplateBuilder()
TemplateBuilder.from_preset(preset_type: ReportPresetType)  # Desde preset
```

**Métodos:**

| Método | Descripción | Retorno |
|--------|-------------|---------|
| `with_colors(primary, secondary)` | Configura colores | `TemplateBuilder` |
| `with_fonts(title_font, body_font)` | Configura fuentes | `TemplateBuilder` |
| `with_margins(top, bottom, left, right)` | Configura márgenes | `TemplateBuilder` |
| `with_page_size(page_size)` | Configura tamaño de página | `TemplateBuilder` |
| `with_header(logo_path, height)` | Configura encabezado | `TemplateBuilder` |
| `with_footer(logo_path, logo_position, ...)` | Configura pie de página | `TemplateBuilder` |
| `with_sections(sections)` | Configura secciones | `TemplateBuilder` |
| `with_cover(cover_image_path)` | Configura imagen de portada | `TemplateBuilder` |
| `with_charts(charts)` | Configura gráficas (máx 10) | `TemplateBuilder` |
| `build()` | Construye ReportTemplate | `ReportTemplate` |

### ChartConfig (v0.1.5)

Configuración para gráficas individuales en reportes.

```python
ChartConfig(
    chart_type: str,           # 'bar', 'barh', 'line', 'pie', 'scatter', 'area'
    title: str,                # Título de la gráfica
    data_query: str = None,    # Query opcional para datos
    group_by: str = None,      # Columna para agrupar
    value_column: str = None,  # Columna de valores
    color: str = None,         # Color hex (ej: "#003366")
    figsize: tuple = (10, 6),  # Tamaño en pulgadas
)
```

**Tipos de gráfica soportados:** `bar`, `barh`, `line`, `pie`, `scatter`, `area`

### ReportPreset (v0.1.5)

Presets predefinidos por industria.

```python
ReportPreset.get(preset_type: ReportPresetType)  # Obtener preset
ReportPreset.list_all()  # Listar todos los presets
preset.to_template(**overrides)  # Convertir a ReportTemplate
```

**Tipos de preset:**

| Tipo | Descripción | Colores |
|------|-------------|---------|
| `FINANCIAL` | Banca e inversiones | Azul (#003366) |
| `HEALTHCARE` | Salud y farmacéutica | Verde/Teal (#006666) |
| `TECHNOLOGY` | Software y TI | Púrpura (#5C2D91) |
| `RETAIL` | Comercio y ventas | Naranja (#E65100) |
| `MANUFACTURING` | Producción e industria | Gris (#455A64) |
| `CONSULTING` | Consultoría y servicios | Azul marino (#1A237E) |

### AIBuilder (v0.1.5)

Agente inteligente para preparación de datos con LangChain.

```python
AIBuilder(df: DataFrame, llm: Any, verbose: bool = False)
```

**Métodos:**

| Método | Descripción | Retorno |
|--------|-------------|---------|
| `get_data_summary()` | Resumen estructurado de datos | `DataSummary` |
| `suggest_charts(context)` | Sugiere gráficas apropiadas | `list[ChartSuggestion]` |
| `prepare_report_data(description)` | Prepara datos para reporte | `dict` |
| `validate_query(query)` | Valida factibilidad de query | `tuple[bool, str]` |
| `ask(query)` | Pregunta sobre los datos | `str` |
| `clear_context()` | Limpia contexto de conversación | `None` |

**Propiedades:**

| Propiedad | Descripción | Tipo |
|-----------|-------------|------|
| `dataframe` | DataFrame subyacente | `pd.DataFrame` |
| `has_langchain` | Si LangChain está disponible | `bool` |

### DataSummary (v0.1.5)

Resumen estructurado de un DataFrame.

```python
@dataclass
class DataSummary:
    columns: list[str]           # Nombres de columnas
    dtypes: dict[str, str]       # Tipos de datos
    shape: tuple[int, int]       # (filas, columnas)
    sample: dict[str, Any]       # Muestra de datos
    numeric_columns: list[str]   # Columnas numéricas
    categorical_columns: list[str]  # Columnas categóricas
    null_counts: dict[str, int]  # Conteo de nulos
```

### ChartSuggestion (v0.1.5)

Sugerencia de gráfica con razonamiento.

```python
@dataclass
class ChartSuggestion:
    config: ChartConfig    # Configuración de la gráfica
    reasoning: str         # Razón de la sugerencia
    confidence: float      # Nivel de confianza (0.0 a 1.0)
```

### TextElement (v0.1.5)

Elemento de texto configurable para portadas.

```python
TextElement(
    content: str,                    # Contenido del texto
    x: float = 0.0,                  # Posición X en puntos
    y: float = 0.0,                  # Posición Y en puntos
    font_size: float = 12.0,         # Tamaño de fuente
    font_family: str = "Helvetica",  # Familia de fuente
    color: str = "#000000",          # Color hex
    alignment: TextAlignment = LEFT, # Alineación
    element_type: str = "custom",    # Tipo de elemento
)
```

### TextAlignment (v0.1.5)

Enum para alineación de texto.

| Valor | Descripción |
|-------|-------------|
| `LEFT` | Alineación izquierda |
| `CENTER` | Centrado |
| `RIGHT` | Alineación derecha |

### Excepciones

| Excepción | Descripción |
|-----------|-------------|
| `QryDocError` | Excepción base para todos los errores |
| `QueryError` | Error al interpretar o ejecutar consulta |
| `ExportError` | Error al exportar datos |
| `ReportError` | Error al generar reporte PDF |
| `DataSourceError` | Error al cargar fuente de datos |
| `ValidationError` | Error de validación de salidas |

Todas las excepciones incluyen:
- `user_message`: Mensaje amigable para mostrar al usuario
- `internal_error`: Excepción original para debugging

```python
from qry_doc import QryDoc, QueryError

try:
    respuesta = qry.ask("consulta ambigua")
except QueryError as e:
    print(f"Error: {e.user_message}")
    # Logging interno: e.internal_error
```

## Configuración avanzada

### Variables de entorno

| Variable | Descripción |
|----------|-------------|
| `OPENAI_API_KEY` | API key de OpenAI (fallback) |

### Encoding de archivos

Por defecto, los CSV se exportan con encoding `utf-8-sig` (BOM) para compatibilidad con Excel. Esto asegura que caracteres especiales (ñ, acentos, etc.) se muestren correctamente.

## Desarrollo

### Requisitos

- Python 3.11+
- [uv](https://docs.astral.sh/uv/) (gestor de paquetes)

### Setup

```bash
git clone https://github.com/danro-dev/qry-doc.git
cd qry-doc
uv sync --all-extras
```

### Tests

```bash
# Ejecutar todos los tests
uv run pytest

# Con cobertura
uv run pytest --cov=src/qry_doc --cov-report=html

# Tests específicos
uv run pytest tests/property/test_qrydoc.py -v
```

### Estructura del proyecto

```
qry-doc/
├── src/qry_doc/
│   ├── __init__.py          # Exports públicos
│   ├── core.py              # QryDoc (Facade)
│   ├── ai_adapter.py        # PandasAI adapter
│   ├── ai_builder.py        # AIBuilder con LangChain (v0.1.5)
│   ├── data_source.py       # Carga de datos
│   ├── csv_exporter.py      # Exportación CSV
│   ├── report_generator.py  # Generación PDF
│   ├── report_template.py   # Templates de reportes
│   ├── chart_manager.py     # Gestión de gráficos
│   ├── chart_config.py      # ChartConfig para multi-gráficas (v0.1.5)
│   ├── cover_builder.py     # CoverBuilder para portadas (v0.1.5)
│   ├── template_builder.py  # TemplateBuilder fluido (v0.1.5)
│   ├── report_preset.py     # ReportPresets por industria (v0.1.5)
│   ├── text_element.py      # TextElement para portadas (v0.1.5)
│   ├── asset_manager.py     # Gestión de assets (v0.2.3+)
│   ├── validators.py        # Validación y sanitización
│   ├── exceptions.py        # Jerarquía de excepciones
│   └── assets/              # Assets del paquete (v0.2.3+)
│       └── default_logo.png # Logo por defecto
├── tests/
│   ├── conftest.py          # Fixtures compartidos
│   └── property/            # Property-based tests
├── docs/                    # Documentación adicional
├── pyproject.toml
└── README.md
```

## Roadmap

- [ ] Soporte para conexiones SQL (PostgreSQL, MySQL, SQLite)
- [ ] Caché de consultas para mejorar rendimiento
- [ ] Exportación a Excel (.xlsx)
- [ ] Más templates predefinidos
- [ ] CLI para uso desde terminal
- [ ] Integración con Jupyter notebooks

## Contribuir

¡Las contribuciones son bienvenidas! Por favor:

1. Fork el repositorio
2. Crea una rama para tu feature (`git checkout -b feature/nueva-funcionalidad`)
3. Commit tus cambios (`git commit -m 'feat: agregar nueva funcionalidad'`)
4. Push a la rama (`git push origin feature/nueva-funcionalidad`)
5. Abre un Pull Request

## Licencia

GPL-3.0 License - ver [LICENSE](LICENSE) para más detalles.

---

<p align="center">
  Hecho con ❤️ por <a href="https://github.com/danro-dev">danro-dev</a>
</p>