176
src/infrastructure/adapters/moderation_repository_mongo.py
Normal file
176
src/infrastructure/adapters/moderation_repository_mongo.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""Adaptador de repositorio de Moderación con MongoDB"""
|
||||
from application.ports.moderation_repository import ModerationRepository
|
||||
from domain.moderations import ModerationAction, ModerationLog
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ModerationRepositoryMongo(ModerationRepository):
|
||||
"""Implementación de ModerationRepository usando MongoDB"""
|
||||
|
||||
def __init__(self):
|
||||
self.db = None
|
||||
self.moderation_actions_collection = None
|
||||
self.moderation_logs_collection = None
|
||||
self._initialize_db()
|
||||
|
||||
def _initialize_db(self):
|
||||
"""Inicializa conexión a MongoDB"""
|
||||
try:
|
||||
from infrastructure.adapters.persistence.mongodb import mongo_db
|
||||
self.db = mongo_db
|
||||
|
||||
# Obtener o crear colecciones
|
||||
self.moderation_actions_collection = self.db['moderation_actions']
|
||||
self.moderation_logs_collection = self.db['moderation_logs']
|
||||
|
||||
# Crear índices
|
||||
self.moderation_actions_collection.create_index('action_id')
|
||||
self.moderation_actions_collection.create_index('moderator_id')
|
||||
self.moderation_actions_collection.create_index('target_id')
|
||||
self.moderation_logs_collection.create_index('action_id')
|
||||
|
||||
logger.info("Moderation MongoDB adapter initialized")
|
||||
except Exception as e:
|
||||
logger.error(f"Error initializing MongoDB for moderations: {e}")
|
||||
raise
|
||||
|
||||
def save_moderation_action(self, action: ModerationAction) -> bool:
|
||||
"""Guarda una acción de moderación en MongoDB"""
|
||||
try:
|
||||
action_dict = {
|
||||
'action_id': action.action_id,
|
||||
'moderator_id': action.moderator_id,
|
||||
'action_type': action.action_type,
|
||||
'target_type': action.target_type,
|
||||
'target_id': action.target_id,
|
||||
'reason': action.reason,
|
||||
'description': action.description,
|
||||
'duration_days': action.duration_days,
|
||||
'is_permanent': action.is_permanent,
|
||||
'status': action.status,
|
||||
'fecha_creacion': action.fecha_creacion or datetime.now(),
|
||||
'fecha_ejecucion': action.fecha_ejecucion,
|
||||
'observaciones': action.observaciones
|
||||
}
|
||||
|
||||
self.moderation_actions_collection.insert_one(action_dict)
|
||||
logger.info(f"Moderation action saved: {action.action_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving moderation action: {e}")
|
||||
return False
|
||||
|
||||
def get_moderation_action(self, action_id: str) -> Optional[ModerationAction]:
|
||||
"""Obtiene una acción de moderación por ID"""
|
||||
try:
|
||||
doc = self.moderation_actions_collection.find_one({'action_id': action_id})
|
||||
if doc:
|
||||
return self._doc_to_moderation_action(doc)
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting moderation action: {e}")
|
||||
return None
|
||||
|
||||
def get_actions_by_moderator(self, moderator_id: int) -> List[ModerationAction]:
|
||||
"""Obtiene todas las acciones de un moderador"""
|
||||
try:
|
||||
docs = self.moderation_actions_collection.find({'moderator_id': moderator_id})
|
||||
return [self._doc_to_moderation_action(doc) for doc in docs]
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting actions by moderator: {e}")
|
||||
return []
|
||||
|
||||
def get_actions_by_target(self, target_id: str, target_type: str) -> List[ModerationAction]:
|
||||
"""Obtiene todas las acciones sobre un target específico"""
|
||||
try:
|
||||
docs = self.moderation_actions_collection.find({
|
||||
'target_id': target_id,
|
||||
'target_type': target_type
|
||||
})
|
||||
return [self._doc_to_moderation_action(doc) for doc in docs]
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting actions by target: {e}")
|
||||
return []
|
||||
|
||||
def update_action_status(self, action_id: str, status: str) -> bool:
|
||||
"""Actualiza el estado de una acción"""
|
||||
try:
|
||||
result = self.moderation_actions_collection.update_one(
|
||||
{'action_id': action_id},
|
||||
{'$set': {'status': status}}
|
||||
)
|
||||
return result.modified_count > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating action status: {e}")
|
||||
return False
|
||||
|
||||
def save_moderation_log(self, log: ModerationLog) -> bool:
|
||||
"""Guarda un log de moderación"""
|
||||
try:
|
||||
log_dict = {
|
||||
'log_id': log.log_id,
|
||||
'action_id': log.action_id,
|
||||
'moderator_id': log.moderator_id,
|
||||
'action_type': log.action_type,
|
||||
'target_type': log.target_type,
|
||||
'target_id': log.target_id,
|
||||
'resultado': log.resultado,
|
||||
'mensaje': log.mensaje,
|
||||
'fecha_log': log.fecha_log or datetime.now(),
|
||||
'ip_moderator': log.ip_moderator
|
||||
}
|
||||
|
||||
self.moderation_logs_collection.insert_one(log_dict)
|
||||
logger.info(f"Moderation log saved: {log.log_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving moderation log: {e}")
|
||||
return False
|
||||
|
||||
def get_moderation_logs(self, action_id: str) -> List[ModerationLog]:
|
||||
"""Obtiene todos los logs de una acción"""
|
||||
try:
|
||||
docs = self.moderation_logs_collection.find({'action_id': action_id})
|
||||
return [self._doc_to_moderation_log(doc) for doc in docs]
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting moderation logs: {e}")
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def _doc_to_moderation_action(doc: dict) -> ModerationAction:
|
||||
"""Convierte un documento de MongoDB a ModerationAction"""
|
||||
return ModerationAction(
|
||||
action_id=doc.get('action_id'),
|
||||
moderator_id=doc.get('moderator_id'),
|
||||
action_type=doc.get('action_type'),
|
||||
target_type=doc.get('target_type'),
|
||||
target_id=doc.get('target_id'),
|
||||
reason=doc.get('reason'),
|
||||
description=doc.get('description'),
|
||||
duration_days=doc.get('duration_days'),
|
||||
is_permanent=doc.get('is_permanent', False),
|
||||
status=doc.get('status', 'pending'),
|
||||
fecha_creacion=doc.get('fecha_creacion'),
|
||||
fecha_ejecucion=doc.get('fecha_ejecucion'),
|
||||
observaciones=doc.get('observaciones')
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _doc_to_moderation_log(doc: dict) -> ModerationLog:
|
||||
"""Convierte un documento de MongoDB a ModerationLog"""
|
||||
return ModerationLog(
|
||||
log_id=doc.get('log_id'),
|
||||
action_id=doc.get('action_id'),
|
||||
moderator_id=doc.get('moderator_id'),
|
||||
action_type=doc.get('action_type'),
|
||||
target_type=doc.get('target_type'),
|
||||
target_id=doc.get('target_id'),
|
||||
resultado=doc.get('resultado'),
|
||||
mensaje=doc.get('mensaje'),
|
||||
fecha_log=doc.get('fecha_log'),
|
||||
ip_moderator=doc.get('ip_moderator')
|
||||
)
|
||||
@@ -0,0 +1,136 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Optional
|
||||
from sqlalchemy import Column, Integer, String, DateTime, JSON, create_engine
|
||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from domain.metrics import Metric, DailyStats, AnalyticsReport, EventType
|
||||
from application.ports.metrics_repository import MetricsRepository
|
||||
from core.config import ConfSettings
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class MetricModel(Base):
|
||||
"""Modelo SQLAlchemy para métricas en Postgres"""
|
||||
__tablename__ = "metrics"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
event_type = Column(String, nullable=False, index=True)
|
||||
entity_id = Column(String, nullable=False)
|
||||
entity_type = Column(String, nullable=False)
|
||||
timestamp = Column(DateTime, nullable=False, default=datetime.now, index=True)
|
||||
metadata = Column(JSON, default={})
|
||||
user_id = Column(Integer, nullable=True)
|
||||
|
||||
|
||||
class MetricsRepositoryPostgres(MetricsRepository):
|
||||
"""Implementación de repositorio de métricas con PostgreSQL"""
|
||||
|
||||
def __init__(self):
|
||||
db_url = f"postgresql://voxpopuli:voxpopuli_pass@localhost:5432/voxpopuli_metrics"
|
||||
self.engine = create_engine(db_url, echo=False)
|
||||
Base.metadata.create_all(self.engine)
|
||||
self.SessionLocal = sessionmaker(bind=self.engine)
|
||||
|
||||
def save_metric(self, metric: Metric) -> Metric:
|
||||
"""Guarda una métrica en la base de datos"""
|
||||
session = self.SessionLocal()
|
||||
try:
|
||||
db_metric = MetricModel(
|
||||
event_type=metric.event_type,
|
||||
entity_id=metric.entity_id,
|
||||
entity_type=metric.entity_type,
|
||||
timestamp=metric.timestamp,
|
||||
metadata=metric.metadata,
|
||||
user_id=metric.user_id
|
||||
)
|
||||
session.add(db_metric)
|
||||
session.commit()
|
||||
metric.metric_id = db_metric.id
|
||||
return metric
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def get_metrics_by_date_range(self, start_date: datetime, end_date: datetime) -> List[Metric]:
|
||||
"""Obtiene métricas en un rango de fechas"""
|
||||
session = self.SessionLocal()
|
||||
try:
|
||||
db_metrics = session.query(MetricModel).filter(
|
||||
MetricModel.timestamp >= start_date,
|
||||
MetricModel.timestamp <= end_date
|
||||
).all()
|
||||
|
||||
return [
|
||||
Metric(
|
||||
metric_id=m.id,
|
||||
event_type=m.event_type,
|
||||
entity_id=m.entity_id,
|
||||
entity_type=m.entity_type,
|
||||
timestamp=m.timestamp,
|
||||
metadata=m.metadata or {},
|
||||
user_id=m.user_id
|
||||
)
|
||||
for m in db_metrics
|
||||
]
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def get_daily_stats(self, date: datetime) -> List[DailyStats]:
|
||||
"""Obtiene estadísticas diarias"""
|
||||
session = self.SessionLocal()
|
||||
try:
|
||||
start = datetime(date.year, date.month, date.day)
|
||||
end = datetime(date.year, date.month, date.day, 23, 59, 59)
|
||||
|
||||
results = session.query(
|
||||
MetricModel.event_type,
|
||||
func.count(MetricModel.id).label('count')
|
||||
).filter(
|
||||
MetricModel.timestamp >= start,
|
||||
MetricModel.timestamp <= end
|
||||
).group_by(MetricModel.event_type).all()
|
||||
|
||||
return [
|
||||
DailyStats(date=date, event_type=r[0], count=r[1])
|
||||
for r in results
|
||||
]
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def get_event_count_by_type(self, start_date: datetime, end_date: datetime) -> Dict[str, int]:
|
||||
"""Obtiene conteo de eventos por tipo"""
|
||||
session = self.SessionLocal()
|
||||
try:
|
||||
results = session.query(
|
||||
MetricModel.event_type,
|
||||
func.count(MetricModel.id).label('count')
|
||||
).filter(
|
||||
MetricModel.timestamp >= start_date,
|
||||
MetricModel.timestamp <= end_date
|
||||
).group_by(MetricModel.event_type).all()
|
||||
|
||||
return {r[0]: r[1] for r in results}
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def generate_report(self, start_date: datetime, end_date: datetime) -> AnalyticsReport:
|
||||
"""Genera reporte de analítica"""
|
||||
session = self.SessionLocal()
|
||||
try:
|
||||
total_events = session.query(func.count(MetricModel.id)).filter(
|
||||
MetricModel.timestamp >= start_date,
|
||||
MetricModel.timestamp <= end_date
|
||||
).scalar() or 0
|
||||
|
||||
events_by_type = self.get_event_count_by_type(start_date, end_date)
|
||||
|
||||
return AnalyticsReport(
|
||||
report_id=None,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
total_events=total_events,
|
||||
events_by_type=events_by_type
|
||||
)
|
||||
finally:
|
||||
session.close()
|
||||
@@ -27,6 +27,15 @@ class NotificationEventType(str, Enum):
|
||||
REPORT_STATUS_CHANGE = "notification.report_status_change"
|
||||
|
||||
|
||||
class ModerationEventType(str, Enum):
|
||||
"""Types of moderation events"""
|
||||
DELETE_REPORT = "moderation.delete_report"
|
||||
CLOSE_ACCOUNT = "moderation.close_account"
|
||||
BAN_USER = "moderation.ban_user"
|
||||
WARN_USER = "moderation.warn_user"
|
||||
REVIEW_CONTENT = "moderation.review_content"
|
||||
|
||||
|
||||
@dataclass
|
||||
class UserMessage:
|
||||
"""Message for user events"""
|
||||
@@ -122,3 +131,36 @@ class NotificationMessage:
|
||||
"""Create from dictionary"""
|
||||
data['event_type'] = NotificationEventType(data['event_type'])
|
||||
return NotificationMessage(**data)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModerationMessage:
|
||||
"""Message for moderation events"""
|
||||
event_type: ModerationEventType
|
||||
action_id: Optional[str] = None
|
||||
moderator_id: Optional[int] = None
|
||||
report_id: Optional[str] = None
|
||||
user_id: Optional[int] = None
|
||||
reason: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
duration_days: Optional[int] = None # Para bans temporales
|
||||
is_permanent: Optional[bool] = None
|
||||
review_action: Optional[str] = None # approve, reject, needs_more_info
|
||||
notes: Optional[str] = None
|
||||
fecha_creacion: Optional[str] = None # ISO format datetime string
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert to dictionary"""
|
||||
data = asdict(self)
|
||||
data['event_type'] = self.event_type.value
|
||||
return data
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Convert to JSON string"""
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: dict) -> 'ModerationMessage':
|
||||
"""Create from dictionary"""
|
||||
data['event_type'] = ModerationEventType(data['event_type'])
|
||||
return ModerationMessage(**data)
|
||||
|
||||
Reference in New Issue
Block a user