Implementación inicial del sistema de automatización de redes sociales

- Estructura completa del proyecto con FastAPI
- Modelos de base de datos (productos, servicios, posts, calendario, interacciones)
- Publishers para X, Threads, Instagram, Facebook
- Generador de contenido con DeepSeek API
- Worker de Celery con tareas programadas
- Dashboard básico con templates HTML
- Docker Compose para despliegue
- Documentación completa

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-28 01:11:44 +00:00
commit 049d2133f9
53 changed files with 5876 additions and 0 deletions

118
app/api/routes/dashboard.py Normal file
View File

@@ -0,0 +1,118 @@
"""
API Routes para el Dashboard.
"""
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from sqlalchemy import func
from app.core.database import get_db
from app.models.post import Post
from app.models.interaction import Interaction
router = APIRouter()
templates = Jinja2Templates(directory="dashboard/templates")
@router.get("/", response_class=HTMLResponse)
async def dashboard_home(request: Request, db: Session = Depends(get_db)):
"""Página principal del dashboard."""
# Estadísticas
now = datetime.utcnow()
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
week_start = today_start - timedelta(days=now.weekday())
stats = {
"posts_today": db.query(Post).filter(
Post.published_at >= today_start
).count(),
"posts_week": db.query(Post).filter(
Post.published_at >= week_start
).count(),
"pending_approval": db.query(Post).filter(
Post.status == "pending_approval"
).count(),
"scheduled": db.query(Post).filter(
Post.status == "scheduled"
).count(),
"interactions_pending": db.query(Interaction).filter(
Interaction.responded == False,
Interaction.is_archived == False
).count()
}
# Posts pendientes
pending_posts = db.query(Post).filter(
Post.status == "pending_approval"
).order_by(Post.scheduled_at.asc()).limit(5).all()
# Próximas publicaciones
scheduled_posts = db.query(Post).filter(
Post.status == "scheduled",
Post.scheduled_at >= now
).order_by(Post.scheduled_at.asc()).limit(5).all()
# Interacciones recientes
recent_interactions = db.query(Interaction).filter(
Interaction.responded == False
).order_by(Interaction.interaction_at.desc()).limit(5).all()
return templates.TemplateResponse("index.html", {
"request": request,
"stats": stats,
"pending_posts": [p.to_dict() for p in pending_posts],
"scheduled_posts": [p.to_dict() for p in scheduled_posts],
"recent_interactions": [i.to_dict() for i in recent_interactions]
})
@router.get("/posts", response_class=HTMLResponse)
async def dashboard_posts(request: Request, db: Session = Depends(get_db)):
"""Página de gestión de posts."""
posts = db.query(Post).order_by(Post.created_at.desc()).limit(50).all()
return templates.TemplateResponse("posts.html", {
"request": request,
"posts": [p.to_dict() for p in posts]
})
@router.get("/calendar", response_class=HTMLResponse)
async def dashboard_calendar(request: Request):
"""Página de calendario."""
return templates.TemplateResponse("calendar.html", {
"request": request
})
@router.get("/interactions", response_class=HTMLResponse)
async def dashboard_interactions(request: Request, db: Session = Depends(get_db)):
"""Página de interacciones."""
interactions = db.query(Interaction).filter(
Interaction.is_archived == False
).order_by(Interaction.interaction_at.desc()).limit(50).all()
return templates.TemplateResponse("interactions.html", {
"request": request,
"interactions": [i.to_dict() for i in interactions]
})
@router.get("/products", response_class=HTMLResponse)
async def dashboard_products(request: Request):
"""Página de productos."""
return templates.TemplateResponse("products.html", {
"request": request
})
@router.get("/services", response_class=HTMLResponse)
async def dashboard_services(request: Request):
"""Página de servicios."""
return templates.TemplateResponse("services.html", {
"request": request
})