- Morning report (8:00 AM): scheduled posts for today + new interactions - Afternoon report (6:00 PM): published count + pending interactions - Reports are sent via Telegram with formatted summaries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
196 lines
7.7 KiB
Python
196 lines
7.7 KiB
Python
"""
|
|
Tareas de reportes diarios por Telegram.
|
|
"""
|
|
|
|
import asyncio
|
|
from datetime import datetime, timedelta
|
|
|
|
from worker.celery_app import celery_app
|
|
from app.core.database import SessionLocal
|
|
from app.models.post import Post
|
|
from app.models.interaction import Interaction
|
|
from app.services.notifications import telegram_notify
|
|
|
|
|
|
def run_async(coro):
|
|
"""Helper para ejecutar coroutines en Celery."""
|
|
loop = asyncio.get_event_loop()
|
|
return loop.run_until_complete(coro)
|
|
|
|
|
|
@celery_app.task(name="worker.tasks.daily_reports.morning_report")
|
|
def morning_report():
|
|
"""
|
|
Reporte matutino: posts programados para hoy + nuevas interacciones.
|
|
Se ejecuta a las 8:00 AM.
|
|
"""
|
|
db = SessionLocal()
|
|
|
|
try:
|
|
now = datetime.utcnow()
|
|
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
today_end = today_start + timedelta(days=1)
|
|
|
|
# Posts programados para hoy
|
|
scheduled_posts = db.query(Post).filter(
|
|
Post.status == "scheduled",
|
|
Post.scheduled_at >= today_start,
|
|
Post.scheduled_at < today_end
|
|
).order_by(Post.scheduled_at).all()
|
|
|
|
# Interacciones de las últimas 24 horas sin responder
|
|
yesterday = now - timedelta(hours=24)
|
|
new_interactions = db.query(Interaction).filter(
|
|
Interaction.interaction_at >= yesterday,
|
|
Interaction.responded == False,
|
|
Interaction.is_archived == False
|
|
).order_by(Interaction.interaction_at.desc()).all()
|
|
|
|
# Construir mensaje
|
|
message = "☀️ *Buenos días! Reporte matutino*\n"
|
|
message += f"📅 {now.strftime('%d/%m/%Y')}\n\n"
|
|
|
|
# Sección de posts programados
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += "📝 *Posts programados para hoy:*\n\n"
|
|
|
|
if scheduled_posts:
|
|
for post in scheduled_posts:
|
|
time_str = post.scheduled_at.strftime("%H:%M")
|
|
platforms = ", ".join(post.platforms) if post.platforms else "N/A"
|
|
content_preview = post.content[:60] + "..." if len(post.content) > 60 else post.content
|
|
# Escapar caracteres especiales de Markdown
|
|
content_preview = content_preview.replace("_", "\\_").replace("*", "\\*")
|
|
|
|
message += f"⏰ *{time_str}* ({platforms})\n"
|
|
message += f" {content_preview}\n\n"
|
|
else:
|
|
message += " _No hay posts programados para hoy_\n\n"
|
|
|
|
# Sección de interacciones
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += "💬 *Nuevas interacciones (24h):*\n\n"
|
|
|
|
if new_interactions:
|
|
# Agrupar por plataforma
|
|
by_platform = {}
|
|
for interaction in new_interactions:
|
|
platform = interaction.platform
|
|
if platform not in by_platform:
|
|
by_platform[platform] = []
|
|
by_platform[platform].append(interaction)
|
|
|
|
for platform, interactions in by_platform.items():
|
|
message += f"📱 *{platform.upper()}* ({len(interactions)})\n"
|
|
for i in interactions[:3]: # Mostrar máximo 3 por plataforma
|
|
content_preview = i.content[:50] + "..." if i.content and len(i.content) > 50 else (i.content or "N/A")
|
|
content_preview = content_preview.replace("_", "\\_").replace("*", "\\*")
|
|
message += f" • @{i.author_username}: {content_preview}\n"
|
|
if len(interactions) > 3:
|
|
message += f" _...y {len(interactions) - 3} más_\n"
|
|
message += "\n"
|
|
else:
|
|
message += " _No hay nuevas interacciones_\n\n"
|
|
|
|
# Resumen
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += f"📊 *Resumen:* {len(scheduled_posts)} posts | {len(new_interactions)} interacciones"
|
|
|
|
# Enviar mensaje
|
|
success = run_async(telegram_notify(message))
|
|
|
|
return f"Reporte matutino enviado: {len(scheduled_posts)} posts, {len(new_interactions)} interacciones"
|
|
|
|
except Exception as e:
|
|
return f"Error en reporte matutino: {e}"
|
|
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@celery_app.task(name="worker.tasks.daily_reports.afternoon_report")
|
|
def afternoon_report():
|
|
"""
|
|
Reporte vespertino: solo actualización de interacciones.
|
|
Se ejecuta a las 6:00 PM.
|
|
"""
|
|
db = SessionLocal()
|
|
|
|
try:
|
|
now = datetime.utcnow()
|
|
|
|
# Interacciones desde la mañana (últimas 10 horas aprox)
|
|
since_morning = now - timedelta(hours=10)
|
|
|
|
new_interactions = db.query(Interaction).filter(
|
|
Interaction.interaction_at >= since_morning,
|
|
Interaction.responded == False,
|
|
Interaction.is_archived == False
|
|
).order_by(Interaction.interaction_at.desc()).all()
|
|
|
|
# Posts publicados hoy
|
|
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
published_today = db.query(Post).filter(
|
|
Post.status == "published",
|
|
Post.published_at >= today_start
|
|
).count()
|
|
|
|
# Posts pendientes para mañana
|
|
tomorrow_start = today_start + timedelta(days=1)
|
|
tomorrow_end = tomorrow_start + timedelta(days=1)
|
|
scheduled_tomorrow = db.query(Post).filter(
|
|
Post.status == "scheduled",
|
|
Post.scheduled_at >= tomorrow_start,
|
|
Post.scheduled_at < tomorrow_end
|
|
).count()
|
|
|
|
# Construir mensaje
|
|
message = "🌆 *Reporte vespertino*\n"
|
|
message += f"📅 {now.strftime('%d/%m/%Y %H:%M')}\n\n"
|
|
|
|
# Resumen del día
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += "📊 *Resumen del día:*\n\n"
|
|
message += f" ✅ Publicados hoy: {published_today}\n"
|
|
message += f" 📅 Programados mañana: {scheduled_tomorrow}\n\n"
|
|
|
|
# Interacciones pendientes
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += "💬 *Interacciones pendientes:*\n\n"
|
|
|
|
if new_interactions:
|
|
by_platform = {}
|
|
for interaction in new_interactions:
|
|
platform = interaction.platform
|
|
if platform not in by_platform:
|
|
by_platform[platform] = []
|
|
by_platform[platform].append(interaction)
|
|
|
|
for platform, interactions in by_platform.items():
|
|
message += f"📱 *{platform.upper()}* ({len(interactions)})\n"
|
|
for i in interactions[:5]: # Mostrar más en el reporte de la tarde
|
|
content_preview = i.content[:50] + "..." if i.content and len(i.content) > 50 else (i.content or "N/A")
|
|
content_preview = content_preview.replace("_", "\\_").replace("*", "\\*")
|
|
message += f" • @{i.author_username}: {content_preview}\n"
|
|
if len(interactions) > 5:
|
|
message += f" _...y {len(interactions) - 5} más_\n"
|
|
message += "\n"
|
|
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += f"⚠️ *{len(new_interactions)} interacciones sin responder*"
|
|
else:
|
|
message += " ✅ _Todas las interacciones han sido atendidas_\n\n"
|
|
message += "━━━━━━━━━━━━━━━━━━━━━\n"
|
|
message += "🎉 *Excelente trabajo hoy!*"
|
|
|
|
# Enviar mensaje
|
|
success = run_async(telegram_notify(message))
|
|
|
|
return f"Reporte vespertino enviado: {published_today} publicados, {len(new_interactions)} interacciones"
|
|
|
|
except Exception as e:
|
|
return f"Error en reporte vespertino: {e}"
|
|
|
|
finally:
|
|
db.close()
|