From e2882ce72b2b9a99e12dc959f394c9743560b56e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Consultor=C3=ADa=20AS?= Date: Wed, 28 Jan 2026 03:17:23 +0000 Subject: [PATCH] docs: Add comprehensive documentation for all new features - FEATURES_OVERVIEW.md: Complete summary of all system features - ANALYTICS.md: Analytics and reporting system documentation - ODOO_INTEGRATION.md: Odoo ERP integration guide - AB_TESTING.md: A/B testing system documentation - CONTENT_RECYCLING.md: Content recycling system docs - THREAD_SERIES.md: Thread series and scheduled posts - IMAGE_TEMPLATES.md: Visual template system documentation Co-Authored-By: Claude Opus 4.5 --- docs/AB_TESTING.md | 297 ++++++++++++++++++++++++++++++ docs/ANALYTICS.md | 264 +++++++++++++++++++++++++++ docs/CONTENT_RECYCLING.md | 267 +++++++++++++++++++++++++++ docs/FEATURES_OVERVIEW.md | 218 ++++++++++++++++++++++ docs/IMAGE_TEMPLATES.md | 350 ++++++++++++++++++++++++++++++++++++ docs/ODOO_INTEGRATION.md | 369 ++++++++++++++++++++++++++++++++++++++ docs/THREAD_SERIES.md | 334 ++++++++++++++++++++++++++++++++++ 7 files changed, 2099 insertions(+) create mode 100644 docs/AB_TESTING.md create mode 100644 docs/ANALYTICS.md create mode 100644 docs/CONTENT_RECYCLING.md create mode 100644 docs/FEATURES_OVERVIEW.md create mode 100644 docs/IMAGE_TEMPLATES.md create mode 100644 docs/ODOO_INTEGRATION.md create mode 100644 docs/THREAD_SERIES.md diff --git a/docs/AB_TESTING.md b/docs/AB_TESTING.md new file mode 100644 index 0000000..06dc880 --- /dev/null +++ b/docs/AB_TESTING.md @@ -0,0 +1,297 @@ +# A/B Testing + +Sistema de pruebas A/B para optimizar contenido y maximizar engagement. + +## Descripción + +El A/B Testing permite comparar diferentes versiones de contenido para determinar cuál tiene mejor rendimiento. El sistema: + +1. Crea múltiples variantes de contenido +2. Publica cada variante +3. Recopila métricas de cada una +4. Determina estadísticamente el ganador + +## Modelos de Datos + +### ABTest + +```python +ABTest: + id: int + name: str + description: str + test_type: str # content, timing, hashtags, image + + platform: str # x, threads, instagram, facebook + status: str # draft, running, completed, cancelled + + started_at: datetime + ended_at: datetime + duration_hours: int # Duración del test (default 24) + + winning_variant_id: int (FK) + confidence_level: float # Nivel de confianza estadística + + min_sample_size: int # Mín impresiones por variante (default 100) + success_metric: str # engagement_rate, likes, comments, shares +``` + +### ABTestVariant + +```python +ABTestVariant: + id: int + test_id: int (FK -> ab_tests) + name: str # A, B, C, D + + content: str + hashtags: JSON + image_url: str + + post_id: int (FK -> posts) + + # Métricas + impressions: int + reach: int + likes: int + comments: int + shares: int + clicks: int + engagement_rate: float + + is_winner: bool + published_at: datetime +``` + +## API Endpoints + +### GET /api/ab-tests/ +Lista todos los tests A/B. + +**Parámetros:** +- `status`: draft, running, completed, cancelled +- `platform`: x, threads, etc. +- `limit` (int, default=20) + +**Respuesta:** +```json +{ + "tests": [ + { + "id": 1, + "name": "Test de copies", + "platform": "x", + "status": "running", + "variants": [...] + } + ], + "count": 5 +} +``` + +### GET /api/ab-tests/{test_id} +Obtiene un test específico con sus variantes. + +### POST /api/ab-tests/ +Crea un nuevo test A/B. + +**Body:** +```json +{ + "name": "Test de copies para tips", + "platform": "x", + "variants": [ + { + "name": "A", + "content": "💡 Tip: Automatiza tus procesos con IA...", + "hashtags": ["#IA", "#Automatización"] + }, + { + "name": "B", + "content": "¿Sabías que la IA puede ahorrarte 10 horas semanales?...", + "hashtags": ["#Productividad", "#Tech"] + } + ], + "test_type": "content", + "duration_hours": 24, + "min_sample_size": 100, + "success_metric": "engagement_rate" +} +``` + +**Validaciones:** +- Mínimo 2 variantes, máximo 4 +- Cada variante debe tener contenido + +### POST /api/ab-tests/{test_id}/start +Inicia un test A/B. + +**Proceso:** +1. Crea posts para cada variante +2. Programa publicación (escalonada cada 5 min) +3. Cambia status a "running" +4. Registra start_time + +**Respuesta:** +```json +{ + "message": "A/B test started successfully", + "success": true, + "test_id": 1, + "post_ids": [101, 102], + "variants_count": 2 +} +``` + +### POST /api/ab-tests/{test_id}/evaluate +Evalúa resultados y determina ganador. + +**Proceso:** +1. Actualiza métricas de cada variante +2. Verifica tamaño de muestra mínimo +3. Ejecuta test estadístico (chi-square) +4. Determina ganador y confianza +5. Si duration_hours pasó, marca como completed + +**Respuesta:** +```json +{ + "success": true, + "winner": { + "variant_id": 1, + "name": "A", + "engagement_rate": 4.5, + "impressions": 1500 + }, + "runner_up": { + "variant_id": 2, + "name": "B", + "engagement_rate": 3.2, + "impressions": 1400 + }, + "confidence_level": 95.5, + "p_value": 0.045, + "test_status": "completed" +} +``` + +**Si datos insuficientes:** +```json +{ + "success": true, + "status": "insufficient_data", + "min_impressions": 50, + "required": 100 +} +``` + +### GET /api/ab-tests/{test_id}/results +Obtiene resultados actuales (sin determinar ganador). + +### POST /api/ab-tests/{test_id}/cancel +Cancela un test en progreso. + +## Tareas Programadas + +### evaluate_ab_tests +- **Frecuencia:** Cada hora +- **Función:** + - Busca tests en estado "running" + - Si pasó duration_hours, evalúa y completa + - Si no, solo actualiza métricas + +## Tipos de Test + +| Tipo | Descripción | +|------|-------------| +| `content` | Prueba diferentes textos | +| `timing` | Prueba diferentes horarios | +| `hashtags` | Prueba diferentes conjuntos de hashtags | +| `image` | Prueba diferentes imágenes | + +## Métricas de Éxito + +| Métrica | Descripción | +|---------|-------------| +| `engagement_rate` | (likes + comments + shares) / impressions | +| `likes` | Total de likes | +| `comments` | Total de comentarios | +| `shares` | Total de compartidos | +| `clicks` | Total de clics (si aplica) | + +## Análisis Estadístico + +El sistema usa el **test chi-cuadrado** para determinar significancia estadística: + +```python +# Tabla de contingencia +[ + [engagements_A, non_engagements_A], + [engagements_B, non_engagements_B] +] + +chi2, p_value, dof, expected = scipy.stats.chi2_contingency(tabla) +confidence = (1 - p_value) * 100 +``` + +### Interpretación + +| p-value | Confianza | Interpretación | +|---------|-----------|----------------| +| < 0.01 | > 99% | Muy significativo | +| < 0.05 | > 95% | Significativo | +| < 0.10 | > 90% | Marginalmente significativo | +| ≥ 0.10 | < 90% | No significativo | + +## Flujo de Trabajo + +``` +1. Crear Test (status: draft) + ↓ +2. Iniciar Test (status: running) + - Crea posts para variantes + - Programa publicación + ↓ +3. Esperar duración + - Sistema recopila métricas cada 15 min + - Evalúa cada hora + ↓ +4. Evaluar Resultados (status: completed) + - Determina ganador estadístico + - Calcula nivel de confianza +``` + +## Ejemplo de Uso + +```python +from app.services.ab_testing_service import ab_testing_service + +# Crear test +test = await ab_testing_service.create_test( + name="Test de engagement", + platform="x", + variants=[ + {"name": "A", "content": "Versión corta y directa"}, + {"name": "B", "content": "Versión más elaborada con detalles"} + ], + duration_hours=48, + min_sample_size=200 +) + +# Iniciar +result = await ab_testing_service.start_test(test.id) + +# Evaluar (después de publicado) +results = await ab_testing_service.evaluate_test(test.id) +if results.get("winner"): + print(f"Ganador: Variante {results['winner']['name']}") + print(f"Confianza: {results['confidence_level']:.1f}%") +``` + +## Mejores Prácticas + +1. **Tamaño de muestra:** Espera al menos 100 impresiones por variante +2. **Una variable:** Prueba solo un elemento a la vez +3. **Duración:** Mínimo 24 horas para datos representativos +4. **Horarios:** Publica variantes en horarios similares +5. **Confianza:** Busca > 95% antes de tomar decisiones diff --git a/docs/ANALYTICS.md b/docs/ANALYTICS.md new file mode 100644 index 0000000..c40d2cf --- /dev/null +++ b/docs/ANALYTICS.md @@ -0,0 +1,264 @@ +# Analytics y Reportes + +Sistema completo de métricas y análisis de rendimiento para optimizar tu estrategia de contenido. + +## Modelos de Datos + +### PostMetrics +Registra métricas de cada post a lo largo del tiempo. + +```python +PostMetrics: + id: int + post_id: int (FK -> posts) + platform: str # x, threads, instagram, facebook + likes: int + comments: int + shares: int + impressions: int + reach: int + saves: int + clicks: int + replies: int + quotes: int + engagement_rate: float + recorded_at: datetime +``` + +### AnalyticsReport +Reportes agregados (diarios, semanales, mensuales). + +```python +AnalyticsReport: + id: int + report_type: str # daily, weekly, monthly + period_start: date + period_end: date + platform: str (opcional) + total_posts: int + total_impressions: int + total_engagements: int + avg_engagement_rate: float + top_posts: JSON # Lista de mejores posts + best_times: JSON # Mejores horarios + content_performance: JSON # Por tipo de contenido + platform_breakdown: JSON # Por plataforma + summary_text: str # Texto para Telegram +``` + +## API Endpoints + +### GET /api/analytics/dashboard +Obtiene datos del dashboard principal. + +**Parámetros:** +- `days` (int, default=30): Período a analizar +- `platform` (str, opcional): Filtrar por plataforma + +**Respuesta:** +```json +{ + "period_days": 30, + "total_posts": 45, + "total_impressions": 125000, + "total_engagements": 3500, + "total_likes": 2800, + "total_comments": 450, + "total_shares": 250, + "avg_engagement_rate": 2.8, + "platform_breakdown": { + "x": {"posts": 20, "engagements": 1500}, + "threads": {"posts": 25, "engagements": 2000} + }, + "content_breakdown": { + "tip_tech": {"posts": 15, "engagements": 1200}, + "producto": {"posts": 10, "engagements": 800} + }, + "pending_interactions": 12 +} +``` + +### GET /api/analytics/top-posts +Obtiene los posts con mejor rendimiento. + +**Parámetros:** +- `days` (int, default=30): Período a analizar +- `limit` (int, default=10): Máximo de posts +- `platform` (str, opcional): Filtrar por plataforma + +**Respuesta:** +```json +{ + "posts": [ + { + "id": 123, + "content": "Tip: Usa IA para...", + "content_type": "tip_tech", + "platforms": ["x", "threads"], + "published_at": "2025-01-15T10:00:00", + "likes": 150, + "comments": 25, + "shares": 30, + "impressions": 5000, + "engagement_rate": 4.1 + } + ], + "count": 10 +} +``` + +### GET /api/analytics/optimal-times +Calcula los mejores horarios para publicar basado en datos históricos. + +**Parámetros:** +- `platform` (str, opcional): Filtrar por plataforma +- `days` (int, default=90): Días de datos a analizar + +**Respuesta:** +```json +{ + "optimal_times": [ + { + "day": 1, + "day_name": "Mar", + "hour": 12, + "hour_formatted": "12:00", + "avg_engagement_rate": 4.5, + "sample_size": 15 + } + ], + "analysis_period_days": 90, + "platform": "x" +} +``` + +### GET /api/analytics/engagement-trend +Obtiene tendencia de engagement para gráficos. + +**Parámetros:** +- `days` (int, default=30): Período +- `platform` (str, opcional): Plataforma + +**Respuesta:** +```json +{ + "trend": [ + { + "date": "2025-01-01", + "posts": 3, + "impressions": 5000, + "engagements": 200 + } + ], + "period_days": 30, + "platform": null +} +``` + +### GET /api/analytics/reports +Obtiene reportes históricos. + +**Parámetros:** +- `report_type` (str): daily, weekly, monthly +- `limit` (int, default=10): Máximo de reportes + +### POST /api/analytics/reports/generate +Genera un nuevo reporte. + +**Parámetros:** +- `report_type` (str): weekly (otros tipos próximamente) + +### POST /api/analytics/reports/send-telegram +Genera y envía reporte por Telegram. + +### GET /api/analytics/posts/{post_id}/metrics +Obtiene métricas detalladas de un post específico. + +**Respuesta:** +```json +{ + "post_id": 123, + "current_metrics": {"likes": 150, "comments": 25}, + "published_at": "2025-01-15T10:00:00", + "platforms": ["x", "threads"], + "metrics_history": [ + { + "recorded_at": "2025-01-15T12:00:00", + "likes": 50, + "comments": 10, + "engagement_rate": 2.5 + } + ] +} +``` + +## Tareas Programadas + +### fetch_post_metrics +- **Frecuencia:** Cada 15 minutos +- **Función:** Obtiene métricas actualizadas de las APIs de cada plataforma +- **Posts afectados:** Publicados en los últimos 7 días + +### generate_weekly_analytics_report +- **Frecuencia:** Domingos a las 9:00 AM +- **Función:** Genera reporte semanal completo +- **Acciones:** + - Calcula métricas agregadas + - Compara con semana anterior + - Genera texto para Telegram + - Envía notificación (si está configurado) + +### recalculate_optimal_times +- **Frecuencia:** Lunes a las 2:00 AM +- **Función:** Recalcula mejores horarios basado en últimos 90 días + +## Dashboard + +Accede al dashboard de analytics en `/dashboard/analytics`. + +### Características: +- **Cards de métricas:** Posts, impresiones, engagements, tasa +- **Gráfico de tendencia:** Chart.js con impresiones y engagements +- **Desglose por plataforma:** Comparativa visual +- **Top posts:** Lista de mejores performers +- **Mejores horarios:** Mapa de calor visual +- **Rendimiento por contenido:** Por tipo de publicación + +## Configuración + +```bash +# .env +ANALYTICS_FETCH_INTERVAL=15 # minutos +TELEGRAM_REPORT_ENABLED=true +TELEGRAM_REPORT_DAY=6 # 0=Lunes, 6=Domingo +``` + +## Ejemplo de Uso + +```python +from app.services.analytics_service import analytics_service + +# Obtener stats del dashboard +stats = await analytics_service.get_dashboard_stats(days=30, platform="x") + +# Obtener top posts +top = await analytics_service.get_top_posts(days=7, limit=5) + +# Generar reporte semanal +report = await analytics_service.generate_weekly_report() +print(report.summary_text) # Texto formateado para Telegram +``` + +## Cálculos + +### Engagement Rate +``` +engagement_rate = (likes + comments + shares) / impressions * 100 +``` + +### Score de Horario Óptimo +``` +Para cada combinación (día, hora): + promedio_engagement = sum(engagement_rates) / count(posts) + ordenar por promedio_engagement descendente +``` diff --git a/docs/CONTENT_RECYCLING.md b/docs/CONTENT_RECYCLING.md new file mode 100644 index 0000000..0a7d7dc --- /dev/null +++ b/docs/CONTENT_RECYCLING.md @@ -0,0 +1,267 @@ +# Reciclaje de Contenido + +Sistema para republica contenido exitoso y maximizar su alcance. + +## Descripción + +El reciclaje de contenido permite: +- Identificar posts con alto engagement +- Republicarlos con modificaciones opcionales +- Trackear rendimiento de versiones recicladas +- Automatizar el proceso de reciclaje + +## Modelo de Datos + +### RecycledPost + +```python +RecycledPost: + id: int + original_post_id: int (FK -> posts) + new_post_id: int (FK -> posts) + + recycle_number: int # 1, 2, 3... (veces reciclado) + + modifications: JSON + # {"content_changed": true, "hashtags_updated": true, "image_changed": false} + + modification_notes: str + + original_engagement_rate: float + new_engagement_rate: float + + status: str # pending, published, cancelled + reason: str # high_performer, evergreen, seasonal, manual + + recycled_at: datetime + scheduled_for: datetime +``` + +### Campos en Post + +```python +# Campos añadidos al modelo Post +is_recyclable: bool # Si puede ser reciclado (default True) +recycled_from_id: int (FK -> posts) # Post original si es reciclado +recycle_count: int # Veces que este post ha sido reciclado +``` + +## Configuración + +```python +# En recycling_service.py +MIN_DAYS_SINCE_PUBLISH = 30 # No reciclar posts recientes +MIN_ENGAGEMENT_RATE = 2.0 # Mínimo 2% engagement +MAX_RECYCLE_COUNT = 3 # Máximo 3 veces por post +``` + +## API Endpoints + +### GET /api/recycling/candidates +Obtiene posts candidatos para reciclar. + +**Parámetros:** +- `platform` (str): Filtrar por plataforma +- `content_type` (str): Filtrar por tipo de contenido +- `min_engagement_rate` (float, default=2.0): Engagement mínimo +- `min_days` (int, default=30): Días desde publicación +- `limit` (int, default=20): Máximo candidatos + +**Respuesta:** +```json +{ + "candidates": [ + { + "id": 123, + "content": "Tip: Usa IA para automatizar...", + "full_content": "...", + "content_type": "tip_tech", + "platforms": ["x"], + "published_at": "2024-12-01T10:00:00", + "days_since_publish": 58, + "engagement_rate": 4.5, + "recycle_count": 0, + "score": 3.8, + "metrics": {"likes": 150, "comments": 30} + } + ], + "count": 10, + "filters": { + "min_engagement_rate": 2.0, + "min_days": 30 + } +} +``` + +### POST /api/recycling/{post_id} +Recicla un post específico. + +**Body (opcional):** +```json +{ + "content": "Versión actualizada del contenido...", + "hashtags": ["#nuevo", "#hashtag"], + "image_url": "https://...", + "scheduled_for": "2025-02-01T10:00:00", + "platforms": ["x", "threads"], + "reason": "high_performer" +} +``` + +**Respuesta:** +```json +{ + "message": "Post recycled successfully", + "success": true, + "new_post_id": 456, + "recycle_record_id": 1, + "scheduled_for": "2025-02-01T10:00:00", + "platforms": ["x", "threads"] +} +``` + +### POST /api/recycling/auto +Reciclaje automático de mejores posts. + +**Parámetros:** +- `platform` (str, opcional): Plataforma específica +- `count` (int, default=1, max=5): Cantidad a reciclar +- `min_engagement_rate` (float, default=2.0) + +**Respuesta:** +```json +{ + "success": true, + "recycled": 2, + "posts": [ + { + "original_id": 123, + "new_post_id": 456, + "engagement_rate": 4.5 + } + ] +} +``` + +### GET /api/recycling/history +Obtiene historial de reciclaje. + +**Parámetros:** +- `original_post_id` (int, opcional): Filtrar por post original +- `limit` (int, default=50) + +### POST /api/recycling/{post_id}/disable +Marca un post como no reciclable. + +## Sistema de Puntuación + +Los candidatos se puntúan con la fórmula: + +```python +score = engagement_rate * recency_factor * recycled_penalty + +donde: + recency_factor = min(days_since_publish / 90, 1.0) + recycled_penalty = 1 - (recycle_count * 0.2) +``` + +### Ejemplo + +| Post | Engagement | Días | Reciclajes | Score | +|------|------------|------|------------|-------| +| A | 4.0% | 60 | 0 | 4.0 × 0.67 × 1.0 = 2.68 | +| B | 5.0% | 90 | 1 | 5.0 × 1.0 × 0.8 = 4.00 | +| C | 3.0% | 45 | 0 | 3.0 × 0.5 × 1.0 = 1.50 | + +Post B tiene el score más alto. + +## Tareas Programadas + +### auto_recycle_content +- **Frecuencia:** Diario a las 2:00 AM +- **Función:** + - Busca 1 post por plataforma con engagement > 3% + - Programa reciclaje automático +- **Plataformas:** x, threads + +## Flujo de Reciclaje + +``` +1. Identificar Candidatos + - Posts con > 30 días de antigüedad + - Engagement rate > 2% + - recycle_count < 3 + - is_recyclable = true + ↓ +2. Seleccionar por Score + - Ordenar por score descendente + ↓ +3. Crear Post Reciclado + - Clonar contenido (o modificar) + - Establecer recycled_from_id + - Programar publicación + ↓ +4. Registrar en RecycledPost + - Guardar relación + - Incrementar recycle_count del original + ↓ +5. Publicar y Trackear + - Comparar new_engagement_rate vs original +``` + +## Razones de Reciclaje + +| Razón | Descripción | +|-------|-------------| +| `high_performer` | Post con engagement alto | +| `evergreen` | Contenido atemporal relevante | +| `seasonal` | Contenido de temporada que regresa | +| `manual` | Reciclado manualmente | + +## Ejemplo de Uso + +```python +from app.services.recycling_service import recycling_service + +# Buscar candidatos +candidates = await recycling_service.find_recyclable_posts( + platform="x", + min_engagement_rate=3.0, + limit=10 +) + +for candidate in candidates: + print(f"Post {candidate['id']}: {candidate['engagement_rate']}% - Score {candidate['score']}") + +# Reciclar manualmente +result = await recycling_service.recycle_post( + post_id=123, + modifications={"content": "Versión actualizada..."}, + reason="evergreen" +) + +# Reciclaje automático +result = await recycling_service.auto_recycle( + platform="x", + count=2 +) +``` + +## Mejores Prácticas + +1. **Esperar suficiente tiempo:** Mínimo 30 días entre reciclajes +2. **Modificar ligeramente:** Actualiza hashtags o intro +3. **No abusar:** Máximo 3 reciclajes por post +4. **Monitorear:** Compara rendimiento nuevo vs original +5. **Contenido evergreen:** Prioriza contenido atemporal + +## Tipos de Contenido Ideales para Reciclar + +| Tipo | Ideal | Razón | +|------|-------|-------| +| Tips técnicos | ✅ | Siempre relevantes | +| Tutoriales | ✅ | Valor educativo permanente | +| Productos | ⚠️ | Si stock disponible | +| Promociones | ❌ | Fechas específicas | +| Efemérides | ❌ | Fechas específicas | +| Noticias | ❌ | Se vuelven obsoletas | diff --git a/docs/FEATURES_OVERVIEW.md b/docs/FEATURES_OVERVIEW.md new file mode 100644 index 0000000..c64c55a --- /dev/null +++ b/docs/FEATURES_OVERVIEW.md @@ -0,0 +1,218 @@ +# Features Overview + +Sistema de automatización de redes sociales para Consultoría AS. + +## Tabla de Contenidos + +1. [Analytics y Reportes](#analytics-y-reportes) +2. [Integración Odoo](#integración-odoo) +3. [A/B Testing](#ab-testing) +4. [Reciclaje de Contenido](#reciclaje-de-contenido) +5. [Thread Series](#thread-series) +6. [Image Templates](#image-templates) + +--- + +## Analytics y Reportes + +Sistema completo de métricas y análisis de rendimiento. + +**Documentación completa:** [ANALYTICS.md](./ANALYTICS.md) + +### Características principales: +- Dashboard interactivo con métricas en tiempo real +- Tracking de engagement por post y plataforma +- Análisis de mejores horarios para publicar +- Reportes semanales automáticos vía Telegram +- Histórico de métricas por post + +### Endpoints API: +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| GET | `/api/analytics/dashboard` | Datos del dashboard | +| GET | `/api/analytics/top-posts` | Posts con mejor rendimiento | +| GET | `/api/analytics/optimal-times` | Mejores horarios | +| GET | `/api/analytics/engagement-trend` | Tendencia de engagement | +| GET | `/api/analytics/reports` | Reportes históricos | +| POST | `/api/analytics/reports/generate` | Generar reporte | +| POST | `/api/analytics/reports/send-telegram` | Enviar reporte a Telegram | + +--- + +## Integración Odoo + +Sincronización bidireccional con Odoo ERP. + +**Documentación completa:** [ODOO_INTEGRATION.md](./ODOO_INTEGRATION.md) + +### Características principales: +- Sincronización de productos desde Odoo +- Sincronización de servicios desde Odoo +- Exportación de leads al CRM de Odoo +- Gestión de leads desde interacciones sociales +- Logs de sincronización + +### Endpoints API: +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| GET | `/api/odoo/status` | Estado de conexión | +| POST | `/api/odoo/sync/products` | Sincronizar productos | +| POST | `/api/odoo/sync/services` | Sincronizar servicios | +| POST | `/api/odoo/sync/leads` | Exportar leads | +| GET | `/api/odoo/sync/logs` | Historial de sync | +| GET | `/api/leads/` | Listar leads | +| POST | `/api/leads/` | Crear lead | +| POST | `/api/leads/from-interaction/{id}` | Lead desde interacción | + +--- + +## A/B Testing + +Sistema de pruebas A/B para optimizar contenido. + +**Documentación completa:** [AB_TESTING.md](./AB_TESTING.md) + +### Características principales: +- Crear tests con 2-4 variantes +- Publicación automática de variantes +- Seguimiento de métricas por variante +- Análisis estadístico (chi-square) +- Determinación automática de ganador + +### Endpoints API: +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| GET | `/api/ab-tests/` | Listar tests | +| POST | `/api/ab-tests/` | Crear test | +| POST | `/api/ab-tests/{id}/start` | Iniciar test | +| POST | `/api/ab-tests/{id}/evaluate` | Evaluar resultados | +| GET | `/api/ab-tests/{id}/results` | Ver resultados | +| POST | `/api/ab-tests/{id}/cancel` | Cancelar test | + +--- + +## Reciclaje de Contenido + +Republica contenido exitoso automáticamente. + +**Documentación completa:** [CONTENT_RECYCLING.md](./CONTENT_RECYCLING.md) + +### Características principales: +- Identificación de posts reciclables por engagement +- Sistema de puntuación para candidatos +- Modificaciones opcionales al reciclar +- Límite de reciclajes por post +- Reciclaje automático programado + +### Endpoints API: +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| GET | `/api/recycling/candidates` | Posts candidatos | +| POST | `/api/recycling/{post_id}` | Reciclar post | +| POST | `/api/recycling/auto` | Reciclaje automático | +| GET | `/api/recycling/history` | Historial | +| POST | `/api/recycling/{post_id}/disable` | Deshabilitar reciclaje | + +--- + +## Thread Series + +Publica hilos de múltiples posts programados. + +**Documentación completa:** [THREAD_SERIES.md](./THREAD_SERIES.md) + +### Características principales: +- Crear series de posts conectados +- Generación con IA +- Programación con intervalos configurables +- Soporte para reply chains +- Seguimiento de progreso + +### Endpoints API: +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| GET | `/api/threads/` | Listar series | +| POST | `/api/threads/` | Crear serie manual | +| POST | `/api/threads/generate` | Generar con IA | +| POST | `/api/threads/{id}/schedule` | Programar serie | +| POST | `/api/threads/{id}/publish-next` | Publicar siguiente | +| POST | `/api/threads/{id}/cancel` | Cancelar serie | + +--- + +## Image Templates + +Sistema de plantillas para generar imágenes. + +**Documentación completa:** [IMAGE_TEMPLATES.md](./IMAGE_TEMPLATES.md) + +### Características principales: +- Plantillas HTML/CSS inline +- Variables dinámicas +- Múltiples tamaños de salida +- Categorías y tipos de plantillas +- Preview antes de generar + +### Endpoints API: +| Método | Endpoint | Descripción | +|--------|----------|-------------| +| GET | `/api/templates/` | Listar plantillas | +| POST | `/api/templates/` | Crear plantilla | +| PUT | `/api/templates/{id}` | Actualizar | +| DELETE | `/api/templates/{id}` | Eliminar | +| POST | `/api/templates/preview` | Previsualizar | + +--- + +## Tareas Programadas (Celery Beat) + +| Tarea | Frecuencia | Descripción | +|-------|------------|-------------| +| `check_scheduled_posts` | Cada minuto | Publica posts programados | +| `check_thread_schedules` | Cada minuto | Publica posts de hilos | +| `fetch_post_metrics` | Cada 15 min | Actualiza métricas | +| `sync_interactions` | Cada 15 min | Sincroniza interacciones | +| `export_leads_to_odoo` | Cada hora | Exporta leads | +| `evaluate_ab_tests` | Cada hora | Evalúa tests A/B | +| `generate_daily_content` | 6:00 AM | Genera contenido | +| `sync_products_from_odoo` | 6:00 AM | Sincroniza productos | +| `sync_services_from_odoo` | 6:05 AM | Sincroniza servicios | +| `auto_recycle_content` | 2:00 AM | Recicla contenido | +| `recalculate_optimal_times` | Lunes 2:00 AM | Recalcula horarios | +| `generate_weekly_report` | Domingo 9:00 AM | Genera reporte | +| `send_daily_summary` | 9:00 PM | Resumen diario | +| `cleanup_old_data` | Domingo 3:00 AM | Limpieza de datos | + +--- + +## Configuración + +Variables de entorno requeridas en `.env`: + +```bash +# Analytics +ANALYTICS_FETCH_INTERVAL=15 +TELEGRAM_REPORT_ENABLED=true +TELEGRAM_REPORT_DAY=6 + +# Odoo +ODOO_URL=https://tuempresa.odoo.com +ODOO_DB=nombre_bd +ODOO_USERNAME=usuario +ODOO_PASSWORD=api_key +ODOO_SYNC_ENABLED=false +``` + +--- + +## Dashboard URLs + +| URL | Descripción | +|-----|-------------| +| `/dashboard` | Panel principal | +| `/dashboard/analytics` | Analytics | +| `/dashboard/leads` | Gestión de leads | +| `/dashboard/posts` | Gestión de posts | +| `/dashboard/calendar` | Calendario | +| `/dashboard/interactions` | Interacciones | +| `/api/docs` | Documentación Swagger | diff --git a/docs/IMAGE_TEMPLATES.md b/docs/IMAGE_TEMPLATES.md new file mode 100644 index 0000000..0b1f6c6 --- /dev/null +++ b/docs/IMAGE_TEMPLATES.md @@ -0,0 +1,350 @@ +# Image Templates + +Sistema de plantillas para generar imágenes dinámicas. + +## Descripción + +Las plantillas de imagen permiten: +- Definir diseños reutilizables en HTML/CSS +- Insertar variables dinámicas +- Generar imágenes para diferentes plataformas +- Mantener consistencia visual + +## Modelo de Datos + +### ImageTemplate + +```python +ImageTemplate: + id: int + name: str + description: str + + category: str # tip, producto, servicio, promocion, etc. + template_type: str # tip_card, product_card, quote, promo, announcement + + # Plantilla + template_file: str # Ruta a archivo (opcional) + html_template: str # HTML inline + + preview_url: str # URL de imagen preview + + # Variables + variables: [str] # ["title", "content", "accent_color"] + + # Configuración de diseño + design_config: JSON + # { + # "width": 1080, + # "height": 1080, + # "background_color": "#1a1a2e", + # "accent_color": "#d4a574", + # "font_family": "Inter" + # } + + # Tamaños de salida + output_sizes: JSON + # { + # "instagram": {"width": 1080, "height": 1080}, + # "x": {"width": 1200, "height": 675}, + # "facebook": {"width": 1200, "height": 630} + # } + + is_active: bool +``` + +## API Endpoints + +### GET /api/templates/ +Lista todas las plantillas. + +**Parámetros:** +- `category`: tip, producto, servicio, etc. +- `template_type`: tip_card, product_card, etc. +- `active_only` (bool, default=true) +- `limit` (int, default=50) + +**Respuesta:** +```json +{ + "templates": [ + { + "id": 1, + "name": "Tip Card Oscuro", + "description": "Tarjeta para tips con fondo oscuro", + "category": "tip", + "template_type": "tip_card", + "variables": ["title", "content", "emoji"], + "preview_url": "/uploads/previews/tip_card_dark.png" + } + ], + "count": 10 +} +``` + +### GET /api/templates/{template_id} +Obtiene una plantilla con detalles completos. + +Incluye `full_html_template` con el HTML completo. + +### POST /api/templates/ +Crea una nueva plantilla. + +**Body:** +```json +{ + "name": "Quote Card", + "description": "Tarjeta para frases motivacionales", + "category": "frase", + "template_type": "quote", + "html_template": "
...
", + "variables": ["quote", "author", "background_image"], + "design_config": { + "width": 1080, + "height": 1080, + "background_color": "#1a1a2e" + }, + "output_sizes": { + "instagram": {"width": 1080, "height": 1080}, + "x": {"width": 1200, "height": 675} + } +} +``` + +**Validación:** +- Requiere `html_template` o `template_file` + +### PUT /api/templates/{template_id} +Actualiza una plantilla. + +### DELETE /api/templates/{template_id} +Elimina una plantilla. + +### POST /api/templates/preview +Genera preview de una plantilla con variables. + +**Body:** +```json +{ + "template_id": 1, + "variables": { + "title": "Tip del día", + "content": "Automatiza tus procesos con IA", + "emoji": "💡" + }, + "output_size": {"width": 1080, "height": 1080} +} +``` + +O con HTML directo: +```json +{ + "html_template": "
{{title}}
", + "variables": { + "title": "Mi título" + } +} +``` + +**Respuesta:** +```json +{ + "rendered_html": "
Mi título
", + "output_size": {"width": 1080, "height": 1080}, + "variables_used": ["title"] +} +``` + +### GET /api/templates/categories/list +Lista categorías disponibles. + +### GET /api/templates/types/list +Lista tipos de plantilla disponibles. + +## Estructura HTML + +### Variables +Las variables se definen con doble llave: `{{variable}}` + +```html +
+ {{emoji}} +

{{title}}

+

{{content}}

+
+``` + +### Ejemplo Completo + +```html + + + + + + +
+ {{emoji}} +

{{title}}

+

{{content}}

+ +
+ + +``` + +## Categorías + +| Categoría | Uso | +|-----------|-----| +| `tip` | Tips tecnológicos | +| `producto` | Fichas de producto | +| `servicio` | Promoción de servicios | +| `promocion` | Ofertas y descuentos | +| `frase` | Frases motivacionales | +| `dato` | Datos curiosos | +| `anuncio` | Anuncios generales | + +## Tipos de Plantilla + +| Tipo | Descripción | +|------|-------------| +| `tip_card` | Tarjeta de tip con emoji | +| `product_card` | Ficha de producto con imagen | +| `quote` | Frase con autor | +| `promo` | Promoción con precio | +| `announcement` | Anuncio destacado | +| `stats` | Estadísticas/números | +| `comparison` | Antes/después o A vs B | + +## Tamaños por Plataforma + +| Plataforma | Ancho | Alto | Ratio | +|------------|-------|------|-------| +| Instagram Post | 1080 | 1080 | 1:1 | +| Instagram Story | 1080 | 1920 | 9:16 | +| X (Twitter) | 1200 | 675 | 16:9 | +| Facebook | 1200 | 630 | 1.91:1 | +| LinkedIn | 1200 | 627 | 1.91:1 | +| Threads | 1080 | 1080 | 1:1 | + +## Ejemplo de Uso + +```python +from app.models.image_template import ImageTemplate +from app.core.database import SessionLocal + +db = SessionLocal() + +# Crear plantilla +template = ImageTemplate( + name="Tip Card Dark", + category="tip", + template_type="tip_card", + html_template=""" +
+

{{title}}

+

{{content}}

+
+ """, + variables=["title", "content"], + design_config={ + "width": 1080, + "height": 1080, + "background_color": "#1a1a2e" + } +) +db.add(template) +db.commit() + +# Renderizar +html = template.html_template +html = html.replace("{{title}}", "Mi Título") +html = html.replace("{{content}}", "Mi contenido") +``` + +## Generación de Imágenes + +Para convertir HTML a imagen se recomienda: + +### Playwright (recomendado) +```python +from playwright.async_api import async_playwright + +async def html_to_image(html: str, width: int, height: int) -> bytes: + async with async_playwright() as p: + browser = await p.chromium.launch() + page = await browser.new_page(viewport={"width": width, "height": height}) + await page.set_content(html) + screenshot = await page.screenshot(type="png") + await browser.close() + return screenshot +``` + +### WeasyPrint (alternativa) +```python +from weasyprint import HTML + +def html_to_pdf(html: str) -> bytes: + return HTML(string=html).write_pdf() +``` + +## Mejores Prácticas + +1. **Fuentes web:** Usa Google Fonts o incluye fuentes inline +2. **Colores consistentes:** Define palette en design_config +3. **Responsive:** Usa unidades relativas cuando sea posible +4. **Contraste:** Asegura legibilidad texto/fondo +5. **Logo:** Incluye branding en todas las plantillas +6. **Variables descriptivas:** `{{product_name}}` mejor que `{{name}}` +7. **Fallbacks:** Define valores por defecto para variables opcionales + +## Integración con Posts + +```python +# Al crear un post con imagen generada +post = Post( + content="Tip del día...", + image_template_id=template.id, # Referencia a la plantilla usada + image_url="/uploads/generated/tip_123.png" +) +``` diff --git a/docs/ODOO_INTEGRATION.md b/docs/ODOO_INTEGRATION.md new file mode 100644 index 0000000..f46d729 --- /dev/null +++ b/docs/ODOO_INTEGRATION.md @@ -0,0 +1,369 @@ +# Integración con Odoo + +Sincronización bidireccional con Odoo ERP para productos, servicios y leads. + +## Descripción General + +La integración permite: +- **Importar** productos y servicios desde Odoo +- **Exportar** leads generados en redes sociales al CRM de Odoo +- **Consultar** resumen de ventas + +## Configuración + +### Variables de Entorno + +```bash +# .env +ODOO_URL=https://tuempresa.odoo.com +ODOO_DB=nombre_base_datos +ODOO_USERNAME=usuario@empresa.com +ODOO_PASSWORD=api_key_o_password +ODOO_SYNC_ENABLED=true # false para deshabilitar +``` + +### Obtener API Key en Odoo + +1. Ir a **Ajustes > Usuarios** +2. Seleccionar tu usuario +3. Pestaña **Claves de API** +4. Crear nueva clave + +## Modelos de Datos + +### Lead +Leads generados desde interacciones en redes sociales. + +```python +Lead: + id: int + interaction_id: int (FK, opcional) + platform: str # x, threads, instagram, facebook, manual + + # Contacto + name: str + email: str + phone: str + company: str + + # Social + username: str + profile_url: str + + # Interés + interest: str + source_content: str + notes: str + products_interested: [int] # IDs de productos + services_interested: [int] # IDs de servicios + + # Estado + status: str # new, contacted, qualified, proposal, won, lost + priority: str # low, medium, high, urgent + + # Odoo + odoo_lead_id: int + synced_to_odoo: bool + odoo_synced_at: datetime +``` + +### OdooSyncLog +Registro de operaciones de sincronización. + +```python +OdooSyncLog: + id: int + sync_type: str # products, services, leads, sales + direction: str # import, export + status: str # started, completed, failed, partial + records_processed: int + records_created: int + records_updated: int + records_failed: int + error_message: str + error_details: JSON + started_at: datetime + completed_at: datetime +``` + +### Campos Odoo en Product/Service + +```python +# En Product +odoo_product_id: int (unique) +odoo_last_synced: datetime + +# En Service +odoo_service_id: int (unique) +odoo_last_synced: datetime +``` + +## API Endpoints + +### Odoo Status + +#### GET /api/odoo/status +Verifica conexión con Odoo. + +**Respuesta exitosa:** +```json +{ + "connected": true, + "version": "17.0", + "uid": 2 +} +``` + +**Sin conexión:** +```json +{ + "connected": false, + "error": "Authentication failed", + "configured": true +} +``` + +### Sincronización + +#### POST /api/odoo/sync/products +Sincroniza productos desde Odoo. + +**Parámetros:** +- `limit` (int, default=100): Máximo de productos + +**Respuesta:** +```json +{ + "message": "Products synced successfully", + "success": true, + "processed": 50, + "created": 10, + "updated": 40, + "failed": 0 +} +``` + +#### POST /api/odoo/sync/services +Sincroniza servicios desde Odoo. + +#### POST /api/odoo/sync/leads +Exporta leads sin sincronizar a Odoo CRM. + +**Respuesta:** +```json +{ + "message": "Leads exported successfully", + "success": true, + "processed": 5, + "created": 5, + "failed": 0 +} +``` + +#### GET /api/odoo/sync/logs +Obtiene historial de sincronizaciones. + +**Parámetros:** +- `limit` (int, default=20) + +#### GET /api/odoo/sales +Obtiene resumen de ventas desde Odoo. + +**Parámetros:** +- `days` (int, default=30) + +**Respuesta:** +```json +{ + "success": true, + "period_days": 30, + "total_orders": 15, + "total_revenue": 125000.00, + "avg_order_value": 8333.33, + "orders": [...] +} +``` + +### Leads API + +#### GET /api/leads/ +Lista leads con filtros. + +**Parámetros:** +- `status`: new, contacted, qualified, proposal, won, lost +- `priority`: low, medium, high, urgent +- `platform`: x, threads, instagram, facebook, manual +- `synced`: true/false (sincronizado a Odoo) +- `limit`, `offset`: Paginación + +#### GET /api/leads/{lead_id} +Obtiene un lead específico. + +#### POST /api/leads/ +Crea lead manualmente. + +**Body:** +```json +{ + "name": "Juan Pérez", + "email": "juan@empresa.com", + "phone": "+52 664 123 4567", + "company": "Empresa SA", + "platform": "manual", + "interest": "Interesado en automatización", + "priority": "high" +} +``` + +#### POST /api/leads/from-interaction/{interaction_id} +Convierte una interacción en lead. + +**Parámetros:** +- `interest` (str): Descripción del interés +- `priority` (str): Prioridad +- `notes` (str): Notas adicionales + +#### PUT /api/leads/{lead_id} +Actualiza un lead. + +#### DELETE /api/leads/{lead_id} +Elimina un lead. + +#### POST /api/leads/{lead_id}/sync-odoo +Sincroniza un lead específico a Odoo. + +**Respuesta:** +```json +{ + "message": "Lead synced to Odoo successfully", + "odoo_lead_id": 1234 +} +``` + +#### GET /api/leads/stats/summary +Obtiene estadísticas de leads. + +**Respuesta:** +```json +{ + "total": 50, + "by_status": { + "new": 20, + "contacted": 15, + "qualified": 10, + "won": 5 + }, + "by_platform": { + "x": 25, + "threads": 15, + "manual": 10 + }, + "by_priority": { + "high": 10, + "medium": 30, + "low": 10 + }, + "unsynced_to_odoo": 8 +} +``` + +## Tareas Programadas + +### sync_products_from_odoo +- **Frecuencia:** Diario a las 6:00 AM +- **Función:** Importa/actualiza productos desde Odoo +- **Límite:** 200 productos por ejecución + +### sync_services_from_odoo +- **Frecuencia:** Diario a las 6:05 AM +- **Función:** Importa/actualiza servicios desde Odoo +- **Límite:** 100 servicios por ejecución + +### export_leads_to_odoo +- **Frecuencia:** Cada hora (minuto 30) +- **Función:** Exporta leads no sincronizados al CRM + +## Dashboard de Leads + +Accede en `/dashboard/leads`. + +### Características: +- Cards de estadísticas (total, nuevos, contactados, etc.) +- Filtros por estado, prioridad y plataforma +- Lista paginada de leads +- Modal para crear/editar leads +- Botones de sincronización individual y masiva + +### Estados de Lead + +| Estado | Descripción | +|--------|-------------| +| `new` | Lead nuevo sin contactar | +| `contacted` | Se ha hecho contacto inicial | +| `qualified` | Lead calificado con potencial | +| `proposal` | Se envió propuesta | +| `won` | Lead convertido a cliente | +| `lost` | Lead perdido | + +### Prioridades + +| Prioridad | Color | +|-----------|-------| +| `urgent` | Rojo | +| `high` | Naranja | +| `medium` | Azul | +| `low` | Gris | + +## Ejemplo de Uso + +```python +from app.services.odoo_service import odoo_service + +# Verificar conexión +status = await odoo_service.test_connection() +if status["connected"]: + print(f"Conectado a Odoo {status['version']}") + +# Sincronizar productos +result = await odoo_service.sync_products(limit=50) +print(f"Productos: {result['created']} nuevos, {result['updated']} actualizados") + +# Exportar leads +result = await odoo_service.export_leads_to_odoo() +print(f"Leads exportados: {result['created']}") + +# Obtener ventas +sales = await odoo_service.get_sales_summary(days=30) +print(f"Ventas: ${sales['total_revenue']:,.2f}") +``` + +## Mapeo de Campos + +### Producto Odoo → Local +| Odoo | Local | +|------|-------| +| id | odoo_product_id | +| name | name | +| description_sale | description | +| list_price | price | +| categ_id | category | +| qty_available | stock | + +### Lead Local → Odoo +| Local | Odoo | +|-------|------| +| interest | name | +| name | contact_name | +| email | email_from | +| phone | phone | +| company | partner_name | +| (generated) | description | +| priority | priority (0-2) | + +## Notas Técnicas + +- La conexión usa XML-RPC (protocolo estándar de Odoo) +- Cada sincronización crea un registro en `odoo_sync_logs` +- Los productos/servicios se identifican por `odoo_product_id`/`odoo_service_id` +- Si un producto local ya existe (mismo ID de Odoo), se actualiza +- Los leads se exportan como tipo "lead" (no "opportunity") diff --git a/docs/THREAD_SERIES.md b/docs/THREAD_SERIES.md new file mode 100644 index 0000000..4a1d2fb --- /dev/null +++ b/docs/THREAD_SERIES.md @@ -0,0 +1,334 @@ +# Thread Series + +Sistema para crear y publicar hilos de múltiples posts conectados. + +## Descripción + +Los Thread Series permiten: +- Crear contenido largo dividido en posts conectados +- Generar hilos con IA +- Programar publicación escalonada +- Mantener cadena de respuestas (reply chain) + +## Modelos de Datos + +### ThreadSeries + +```python +ThreadSeries: + id: int + name: str + description: str + topic: str + + platform: str # x, threads + + # Configuración de programación + schedule_type: str # sequential, timed + interval_minutes: int # Tiempo entre posts (default 5) + start_time: datetime + + # Estado + total_posts: int + posts_published: int + status: str # draft, scheduled, publishing, completed, paused, cancelled + + # Reply chain + first_platform_post_id: str # ID del primer post (para replies) + + # IA + ai_generated: bool + generation_prompt: str + + hashtags: JSON # Hashtags comunes +``` + +### ThreadPost + +```python +ThreadPost: + id: int + series_id: int (FK -> thread_series) + sequence_number: int # 1, 2, 3... + + content: str + image_url: str + + post_id: int (FK -> posts) + + # Para reply chain + platform_post_id: str + reply_to_platform_id: str + + scheduled_at: datetime + status: str # pending, scheduled, published, failed + error_message: str + published_at: datetime +``` + +## API Endpoints + +### GET /api/threads/ +Lista todas las series. + +**Parámetros:** +- `status`: draft, scheduled, publishing, completed +- `platform`: x, threads +- `limit` (int, default=20) + +### GET /api/threads/{series_id} +Obtiene una serie con todos sus posts. + +**Respuesta:** +```json +{ + "id": 1, + "name": "Hilo sobre IA", + "topic": "Inteligencia Artificial", + "platform": "x", + "status": "scheduled", + "total_posts": 5, + "posts_published": 0, + "start_time": "2025-02-01T10:00:00", + "interval_minutes": 5, + "posts": [ + { + "id": 1, + "sequence_number": 1, + "content": "🧵 Hilo: Todo sobre IA...", + "status": "scheduled", + "scheduled_at": "2025-02-01T10:00:00" + }, + { + "id": 2, + "sequence_number": 2, + "content": "1/ La IA está transformando...", + "status": "scheduled", + "scheduled_at": "2025-02-01T10:05:00" + } + ] +} +``` + +### POST /api/threads/ +Crea una serie manualmente. + +**Body:** +```json +{ + "name": "Tips de productividad", + "platform": "x", + "posts": [ + {"content": "🧵 Hilo: 5 tips para ser más productivo"}, + {"content": "1/ Usa la técnica Pomodoro..."}, + {"content": "2/ Elimina distracciones..."}, + {"content": "3/ Prioriza con Eisenhower..."}, + {"content": "4/ Automatiza tareas repetitivas..."}, + {"content": "5/ Revisa tu progreso diario..."} + ], + "description": "Tips de productividad para profesionales", + "schedule_type": "sequential", + "interval_minutes": 3, + "hashtags": ["#Productividad", "#Tips"] +} +``` + +**Validaciones:** +- Mínimo 2 posts, máximo 20 +- Platform requerido + +### POST /api/threads/generate +Genera un hilo con IA. + +**Body:** +```json +{ + "topic": "Cómo empezar con inteligencia artificial", + "platform": "x", + "num_posts": 5, + "style": "educational", + "name": "Intro a IA" // Opcional: si se incluye, guarda la serie +} +``` + +**Estilos disponibles:** +- `educational`: Contenido informativo y didáctico +- `storytelling`: Formato narrativo +- `tips`: Lista de consejos prácticos + +**Respuesta (sin name):** +```json +{ + "message": "Thread generated (not saved)", + "generated_posts": [ + {"sequence": 1, "content": "🧵 Te explico qué es la IA..."}, + {"sequence": 2, "content": "1/ La IA es..."} + ], + "count": 5 +} +``` + +**Respuesta (con name):** +```json +{ + "message": "Thread generated and saved", + "series": {...}, + "generated_posts": [...] +} +``` + +### POST /api/threads/{series_id}/schedule +Programa una serie para publicación. + +**Body (opcional):** +```json +{ + "start_time": "2025-02-01T10:00:00" +} +``` + +Si no se especifica, inicia en 5 minutos. + +**Proceso:** +1. Crea posts reales para cada ThreadPost +2. Programa cada uno con el intervalo configurado +3. Cambia status a "scheduled" + +**Respuesta:** +```json +{ + "message": "Series scheduled successfully", + "success": true, + "series_id": 1, + "start_time": "2025-02-01T10:00:00", + "posts_scheduled": 5 +} +``` + +### POST /api/threads/{series_id}/publish-next +Publica manualmente el siguiente post de la serie. + +### POST /api/threads/{series_id}/cancel +Cancela una serie y sus posts programados. + +## Tareas Programadas + +### check_thread_schedules +- **Frecuencia:** Cada minuto +- **Función:** + - Busca ThreadPosts programados para ahora + - Encola tarea de publicación + - Actualiza estado + +### update_thread_post_status +- **Trigger:** Después de publicar un post +- **Función:** + - Actualiza estado del ThreadPost + - Guarda platform_post_id (para reply chain) + - Actualiza progreso de la serie + - Marca serie como completed si todos publicados + +## Tipos de Programación + +| Tipo | Descripción | +|------|-------------| +| `sequential` | Posts uno tras otro con intervalo fijo | +| `timed` | Posts en horarios específicos (próximamente) | + +## Reply Chain + +Para que los posts aparezcan como respuestas: + +1. Se publica el primer post +2. Se guarda su `platform_post_id` +3. Los siguientes posts se publican como respuesta al anterior +4. Cada uno guarda su `reply_to_platform_id` + +``` +Post 1 (original) + └── Post 2 (reply to 1) + └── Post 3 (reply to 2) + └── Post 4 (reply to 3) +``` + +## Ejemplo de Uso + +```python +from app.services.thread_service import thread_service + +# Crear serie manual +series = await thread_service.create_series( + name="Tips de Python", + platform="x", + posts_content=[ + {"content": "🧵 Hilo: 5 tips de Python que debes conocer"}, + {"content": "1/ List comprehensions..."}, + {"content": "2/ F-strings..."}, + {"content": "3/ Context managers..."}, + {"content": "4/ Generators..."}, + {"content": "5/ Decorators..."} + ], + interval_minutes=3 +) + +# Generar con IA +result = await thread_service.generate_thread_with_ai( + topic="Beneficios de la automatización", + platform="threads", + num_posts=5, + style="storytelling" +) + +# Programar +await thread_service.schedule_series( + series_id=series.id, + start_time=datetime(2025, 2, 1, 10, 0) +) + +# Ver estado +series = await thread_service.get_series(series.id) +print(f"Publicados: {series['posts_published']}/{series['total_posts']}") +``` + +## Flujo Completo + +``` +1. Crear Serie + - Manual: Proporcionar posts + - IA: Generar con prompt + ↓ +2. Revisar y Editar + - Verificar contenido + - Ajustar hashtags + ↓ +3. Programar + - Establecer start_time + - Crear posts reales + ↓ +4. Publicación Automática + - Celery Beat cada minuto + - Publica posts programados + - Mantiene reply chain + ↓ +5. Completado + - Todos los posts publicados + - Status: completed +``` + +## Mejores Prácticas + +1. **Primer post atractivo:** El 1/5 o 🧵 debe captar atención +2. **Numeración clara:** "1/", "2/" o "1.", "2." +3. **Intervalos cortos:** 3-5 minutos mantienen engagement +4. **Último post con CTA:** Llamado a la acción al final +5. **Hashtags solo al final:** No saturar cada post +6. **Máximo 10-15 posts:** Hilos muy largos pierden audiencia + +## Plataformas Soportadas + +| Plataforma | Reply Chain | Notas | +|------------|-------------|-------| +| X (Twitter) | ✅ | Ideal para threads | +| Threads | ✅ | Buen soporte | +| Instagram | ❌ | No soporta threads | +| Facebook | ❌ | No soporta threads |