Files
social-media-automation/app/api/routes/posts.py
Consultoría AS 049d2133f9 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>
2026-01-28 01:11:44 +00:00

217 lines
6.4 KiB
Python

"""
API Routes para gestión de Posts.
"""
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.post import Post
router = APIRouter()
# ===========================================
# SCHEMAS
# ===========================================
class PostCreate(BaseModel):
content: str
content_type: str
platforms: List[str]
scheduled_at: Optional[datetime] = None
image_url: Optional[str] = None
approval_required: bool = False
hashtags: Optional[List[str]] = None
class Config:
from_attributes = True
class PostUpdate(BaseModel):
content: Optional[str] = None
content_x: Optional[str] = None
content_threads: Optional[str] = None
content_instagram: Optional[str] = None
content_facebook: Optional[str] = None
platforms: Optional[List[str]] = None
scheduled_at: Optional[datetime] = None
status: Optional[str] = None
image_url: Optional[str] = None
hashtags: Optional[List[str]] = None
class Config:
from_attributes = True
class PostResponse(BaseModel):
id: int
content: str
content_type: str
platforms: List[str]
status: str
scheduled_at: Optional[datetime]
published_at: Optional[datetime]
image_url: Optional[str]
approval_required: bool
created_at: datetime
class Config:
from_attributes = True
# ===========================================
# ENDPOINTS
# ===========================================
@router.get("/", response_model=List[PostResponse])
async def list_posts(
status: Optional[str] = Query(None, description="Filtrar por estado"),
content_type: Optional[str] = Query(None, description="Filtrar por tipo"),
platform: Optional[str] = Query(None, description="Filtrar por plataforma"),
limit: int = Query(50, le=100),
offset: int = Query(0),
db: Session = Depends(get_db)
):
"""Listar posts con filtros opcionales."""
query = db.query(Post)
if status:
query = query.filter(Post.status == status)
if content_type:
query = query.filter(Post.content_type == content_type)
if platform:
query = query.filter(Post.platforms.contains([platform]))
posts = query.order_by(Post.created_at.desc()).offset(offset).limit(limit).all()
return posts
@router.get("/pending")
async def list_pending_posts(db: Session = Depends(get_db)):
"""Listar posts pendientes de aprobación."""
posts = db.query(Post).filter(
Post.status == "pending_approval"
).order_by(Post.scheduled_at.asc()).all()
return [post.to_dict() for post in posts]
@router.get("/scheduled")
async def list_scheduled_posts(db: Session = Depends(get_db)):
"""Listar posts programados."""
posts = db.query(Post).filter(
Post.status == "scheduled",
Post.scheduled_at >= datetime.utcnow()
).order_by(Post.scheduled_at.asc()).all()
return [post.to_dict() for post in posts]
@router.get("/{post_id}")
async def get_post(post_id: int, db: Session = Depends(get_db)):
"""Obtener un post por ID."""
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post no encontrado")
return post.to_dict()
@router.post("/", response_model=PostResponse)
async def create_post(post_data: PostCreate, db: Session = Depends(get_db)):
"""Crear un nuevo post."""
post = Post(
content=post_data.content,
content_type=post_data.content_type,
platforms=post_data.platforms,
scheduled_at=post_data.scheduled_at,
image_url=post_data.image_url,
approval_required=post_data.approval_required,
hashtags=post_data.hashtags,
status="pending_approval" if post_data.approval_required else "scheduled"
)
db.add(post)
db.commit()
db.refresh(post)
return post
@router.put("/{post_id}")
async def update_post(post_id: int, post_data: PostUpdate, db: Session = Depends(get_db)):
"""Actualizar un post."""
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post no encontrado")
update_data = post_data.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(post, field, value)
post.updated_at = datetime.utcnow()
db.commit()
db.refresh(post)
return post.to_dict()
@router.post("/{post_id}/approve")
async def approve_post(post_id: int, db: Session = Depends(get_db)):
"""Aprobar un post pendiente."""
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post no encontrado")
if post.status != "pending_approval":
raise HTTPException(status_code=400, detail="El post no está pendiente de aprobación")
post.status = "scheduled"
post.approved_at = datetime.utcnow()
db.commit()
return {"message": "Post aprobado", "post_id": post_id}
@router.post("/{post_id}/reject")
async def reject_post(post_id: int, db: Session = Depends(get_db)):
"""Rechazar un post pendiente."""
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post no encontrado")
post.status = "cancelled"
db.commit()
return {"message": "Post rechazado", "post_id": post_id}
@router.post("/{post_id}/regenerate")
async def regenerate_post(post_id: int, db: Session = Depends(get_db)):
"""Regenerar contenido de un post con IA."""
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post no encontrado")
# TODO: Implementar regeneración con DeepSeek
# from app.services.content_generator import regenerate_content
# new_content = await regenerate_content(post)
return {"message": "Regeneración en desarrollo", "post_id": post_id}
@router.delete("/{post_id}")
async def delete_post(post_id: int, db: Session = Depends(get_db)):
"""Eliminar un post."""
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post no encontrado")
db.delete(post)
db.commit()
return {"message": "Post eliminado", "post_id": post_id}