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

174
app/api/routes/services.py Normal file
View File

@@ -0,0 +1,174 @@
"""
API Routes para gestión de Servicios.
"""
from datetime import datetime
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from pydantic import BaseModel
from app.core.database import get_db
from app.models.service import Service
router = APIRouter()
# ===========================================
# SCHEMAS
# ===========================================
class ServiceCreate(BaseModel):
name: str
description: str
short_description: Optional[str] = None
category: str
target_sectors: Optional[List[str]] = None
benefits: Optional[List[str]] = None
features: Optional[List[str]] = None
case_studies: Optional[List[dict]] = None
icon: Optional[str] = None
images: Optional[List[str]] = None
main_image: Optional[str] = None
price_range: Optional[str] = None
has_free_demo: bool = False
tags: Optional[List[str]] = None
call_to_action: Optional[str] = None
is_featured: bool = False
class Config:
from_attributes = True
class ServiceUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
short_description: Optional[str] = None
category: Optional[str] = None
target_sectors: Optional[List[str]] = None
benefits: Optional[List[str]] = None
features: Optional[List[str]] = None
case_studies: Optional[List[dict]] = None
icon: Optional[str] = None
images: Optional[List[str]] = None
main_image: Optional[str] = None
price_range: Optional[str] = None
has_free_demo: Optional[bool] = None
tags: Optional[List[str]] = None
call_to_action: Optional[str] = None
is_active: Optional[bool] = None
is_featured: Optional[bool] = None
class Config:
from_attributes = True
# ===========================================
# ENDPOINTS
# ===========================================
@router.get("/")
async def list_services(
category: Optional[str] = Query(None),
target_sector: Optional[str] = Query(None),
is_active: Optional[bool] = Query(True),
is_featured: Optional[bool] = Query(None),
limit: int = Query(50, le=100),
offset: int = Query(0),
db: Session = Depends(get_db)
):
"""Listar servicios con filtros."""
query = db.query(Service)
if category:
query = query.filter(Service.category == category)
if is_active is not None:
query = query.filter(Service.is_active == is_active)
if is_featured is not None:
query = query.filter(Service.is_featured == is_featured)
if target_sector:
query = query.filter(Service.target_sectors.contains([target_sector]))
services = query.order_by(Service.created_at.desc()).offset(offset).limit(limit).all()
return [s.to_dict() for s in services]
@router.get("/categories")
async def list_categories(db: Session = Depends(get_db)):
"""Listar categorías únicas de servicios."""
categories = db.query(Service.category).distinct().all()
return [c[0] for c in categories if c[0]]
@router.get("/featured")
async def list_featured_services(db: Session = Depends(get_db)):
"""Listar servicios destacados."""
services = db.query(Service).filter(
Service.is_featured == True,
Service.is_active == True
).all()
return [s.to_dict() for s in services]
@router.get("/{service_id}")
async def get_service(service_id: int, db: Session = Depends(get_db)):
"""Obtener un servicio por ID."""
service = db.query(Service).filter(Service.id == service_id).first()
if not service:
raise HTTPException(status_code=404, detail="Servicio no encontrado")
return service.to_dict()
@router.post("/")
async def create_service(service_data: ServiceCreate, db: Session = Depends(get_db)):
"""Crear un nuevo servicio."""
service = Service(**service_data.dict())
db.add(service)
db.commit()
db.refresh(service)
return service.to_dict()
@router.put("/{service_id}")
async def update_service(service_id: int, service_data: ServiceUpdate, db: Session = Depends(get_db)):
"""Actualizar un servicio."""
service = db.query(Service).filter(Service.id == service_id).first()
if not service:
raise HTTPException(status_code=404, detail="Servicio no encontrado")
update_data = service_data.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(service, field, value)
service.updated_at = datetime.utcnow()
db.commit()
db.refresh(service)
return service.to_dict()
@router.delete("/{service_id}")
async def delete_service(service_id: int, db: Session = Depends(get_db)):
"""Eliminar un servicio."""
service = db.query(Service).filter(Service.id == service_id).first()
if not service:
raise HTTPException(status_code=404, detail="Servicio no encontrado")
db.delete(service)
db.commit()
return {"message": "Servicio eliminado", "service_id": service_id}
@router.post("/{service_id}/toggle-featured")
async def toggle_featured(service_id: int, db: Session = Depends(get_db)):
"""Alternar estado de servicio destacado."""
service = db.query(Service).filter(Service.id == service_id).first()
if not service:
raise HTTPException(status_code=404, detail="Servicio no encontrado")
service.is_featured = not service.is_featured
db.commit()
return {
"message": f"Servicio {'destacado' if service.is_featured else 'no destacado'}",
"is_featured": service.is_featured
}