371 lines
12 KiB
Python
371 lines
12 KiB
Python
"""
|
|
Maintenance Tasks - ServiceManagerWeb Workers
|
|
|
|
Tareas de mantenimiento del sistema
|
|
"""
|
|
|
|
from celery import current_task
|
|
from datetime import datetime, timedelta
|
|
from typing import Dict, Any, List
|
|
import os
|
|
import structlog
|
|
|
|
from app.celery import celery_app
|
|
from app.core.config import get_settings
|
|
from app.core.logging import get_logger
|
|
|
|
settings = get_settings()
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
@celery_app.task(bind=True)
|
|
def health_check(self) -> Dict[str, Any]:
|
|
"""
|
|
Worker health check task.
|
|
|
|
Returns basic health information about the worker.
|
|
"""
|
|
task_logger = logger.bind(
|
|
task_id=self.request.id,
|
|
task_name=self.name
|
|
)
|
|
|
|
try:
|
|
current_time = datetime.utcnow()
|
|
|
|
# Basic health checks
|
|
health_data = {
|
|
"status": "healthy",
|
|
"timestamp": current_time.isoformat(),
|
|
"worker_id": self.request.hostname,
|
|
"task_id": self.request.id,
|
|
"environment": settings.ENVIRONMENT,
|
|
"checks": {
|
|
"redis": "unknown", # TODO: Check Redis connectivity
|
|
"database": "unknown", # TODO: Check database connectivity
|
|
"disk_space": "unknown", # TODO: Check disk space
|
|
"memory": "unknown" # TODO: Check memory usage
|
|
}
|
|
}
|
|
|
|
task_logger.info("Worker health check completed", status="healthy")
|
|
|
|
return health_data
|
|
|
|
except Exception as exc:
|
|
task_logger.error(
|
|
"Worker health check failed",
|
|
error=str(exc),
|
|
exc_info=True
|
|
)
|
|
|
|
return {
|
|
"status": "unhealthy",
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"error": str(exc)
|
|
}
|
|
|
|
|
|
@celery_app.task(
|
|
bind=True,
|
|
time_limit=1800, # 30 minutes
|
|
soft_time_limit=1500 # 25 minutes
|
|
)
|
|
def cleanup_old_logs(self) -> Dict[str, Any]:
|
|
"""
|
|
Clean up old log files and database records.
|
|
|
|
Removes:
|
|
- Log files older than LOG_RETENTION_DAYS
|
|
- Old notification logs
|
|
- Old audit logs (if configured)
|
|
- Temp files
|
|
"""
|
|
task_logger = logger.bind(
|
|
task_id=self.request.id,
|
|
task_name=self.name
|
|
)
|
|
|
|
task_logger.info("Starting cleanup of old logs")
|
|
|
|
try:
|
|
current_time = datetime.utcnow()
|
|
cutoff_date = current_time - timedelta(days=settings.LOG_RETENTION_DAYS)
|
|
|
|
cleanup_results = {
|
|
"started_at": current_time.isoformat(),
|
|
"cutoff_date": cutoff_date.isoformat(),
|
|
"files_removed": 0,
|
|
"bytes_freed": 0,
|
|
"database_records_removed": 0,
|
|
"errors": []
|
|
}
|
|
|
|
# TODO: Implement actual file cleanup
|
|
# For now, simulate cleanup
|
|
|
|
# Clean up log files
|
|
log_dir = "/app/logs"
|
|
if os.path.exists(log_dir):
|
|
for filename in os.listdir(log_dir):
|
|
filepath = os.path.join(log_dir, filename)
|
|
if os.path.isfile(filepath):
|
|
file_mtime = datetime.fromtimestamp(os.path.getmtime(filepath))
|
|
if file_mtime < cutoff_date and filename.endswith('.log'):
|
|
try:
|
|
file_size = os.path.getsize(filepath)
|
|
os.remove(filepath)
|
|
cleanup_results["files_removed"] += 1
|
|
cleanup_results["bytes_freed"] += file_size
|
|
task_logger.info(f"Removed old log file", filename=filename)
|
|
except Exception as e:
|
|
cleanup_results["errors"].append(f"Failed to remove {filename}: {str(e)}")
|
|
|
|
# TODO: Clean up database records
|
|
# - Old notification_logs
|
|
# - Old audit_logs (with retention policy)
|
|
# - Expired refresh_tokens
|
|
# - Old file attachments (if configured)
|
|
|
|
task_logger.info(
|
|
"Cleanup completed",
|
|
files_removed=cleanup_results["files_removed"],
|
|
bytes_freed=cleanup_results["bytes_freed"],
|
|
errors=len(cleanup_results["errors"])
|
|
)
|
|
|
|
return cleanup_results
|
|
|
|
except Exception as exc:
|
|
task_logger.error(
|
|
"Cleanup task failed",
|
|
error=str(exc),
|
|
exc_info=True
|
|
)
|
|
raise
|
|
|
|
|
|
@celery_app.task(
|
|
bind=True,
|
|
time_limit=3600, # 1 hour
|
|
soft_time_limit=3300 # 55 minutes
|
|
)
|
|
def generate_weekly_reports(self) -> Dict[str, Any]:
|
|
"""
|
|
Generate weekly reports for all tenants.
|
|
|
|
Creates:
|
|
- SLA performance reports
|
|
- Ticket volume reports
|
|
- Agent performance reports
|
|
- Customer satisfaction reports
|
|
"""
|
|
task_logger = logger.bind(
|
|
task_id=self.request.id,
|
|
task_name=self.name
|
|
)
|
|
|
|
task_logger.info("Starting weekly reports generation")
|
|
|
|
try:
|
|
current_time = datetime.utcnow()
|
|
week_start = current_time - timedelta(days=7)
|
|
|
|
report_results = {
|
|
"generated_at": current_time.isoformat(),
|
|
"period_start": week_start.isoformat(),
|
|
"period_end": current_time.isoformat(),
|
|
"reports_generated": [],
|
|
"errors": []
|
|
}
|
|
|
|
# TODO: Get list of active tenants from database
|
|
mock_tenants = [
|
|
{"id": "tenant-1", "name": "Aduanasoft Demo", "slug": "aduanasoft-demo"}
|
|
]
|
|
|
|
for tenant in mock_tenants:
|
|
try:
|
|
task_logger.info(
|
|
"Generating report for tenant",
|
|
tenant_id=tenant["id"],
|
|
tenant_name=tenant["name"]
|
|
)
|
|
|
|
# TODO: Generate actual reports
|
|
# For now, simulate report generation
|
|
|
|
report_data = {
|
|
"tenant_id": tenant["id"],
|
|
"tenant_name": tenant["name"],
|
|
"period_start": week_start.isoformat(),
|
|
"period_end": current_time.isoformat(),
|
|
"metrics": {
|
|
"tickets_created": 25,
|
|
"tickets_resolved": 23,
|
|
"avg_response_time_hours": 2.1,
|
|
"avg_resolution_time_hours": 18.5,
|
|
"sla_response_met_percentage": 92.0,
|
|
"sla_resolution_met_percentage": 87.0,
|
|
"customer_satisfaction_avg": 4.2
|
|
}
|
|
}
|
|
|
|
report_results["reports_generated"].append(report_data)
|
|
|
|
# TODO: Store report in database
|
|
# TODO: Send report email to admins
|
|
|
|
# Update task progress
|
|
current_task.update_state(
|
|
state='PROGRESS',
|
|
meta={
|
|
'current': len(report_results["reports_generated"]),
|
|
'total': len(mock_tenants)
|
|
}
|
|
)
|
|
|
|
except Exception as e:
|
|
error_msg = f"Failed to generate report for tenant {tenant['id']}: {str(e)}"
|
|
report_results["errors"].append(error_msg)
|
|
task_logger.error(
|
|
"Report generation failed for tenant",
|
|
tenant_id=tenant["id"],
|
|
error=str(e)
|
|
)
|
|
|
|
task_logger.info(
|
|
"Weekly reports generation completed",
|
|
reports_generated=len(report_results["reports_generated"]),
|
|
errors=len(report_results["errors"])
|
|
)
|
|
|
|
return report_results
|
|
|
|
except Exception as exc:
|
|
task_logger.error(
|
|
"Weekly reports generation failed",
|
|
error=str(exc),
|
|
exc_info=True
|
|
)
|
|
raise
|
|
|
|
|
|
@celery_app.task(
|
|
bind=True,
|
|
time_limit=900, # 15 minutes
|
|
soft_time_limit=780 # 13 minutes
|
|
)
|
|
def cleanup_temp_files(self) -> Dict[str, Any]:
|
|
"""
|
|
Clean up temporary files and orphaned uploads.
|
|
|
|
Removes:
|
|
- Temp upload files older than 24 hours
|
|
- Orphaned attachment files (no DB reference)
|
|
- Processing artifacts
|
|
"""
|
|
task_logger = logger.bind(
|
|
task_id=self.request.id,
|
|
task_name=self.name
|
|
)
|
|
|
|
task_logger.info("Starting temp files cleanup")
|
|
|
|
try:
|
|
current_time = datetime.utcnow()
|
|
cutoff_date = current_time - timedelta(hours=24)
|
|
|
|
cleanup_results = {
|
|
"started_at": current_time.isoformat(),
|
|
"temp_files_removed": 0,
|
|
"orphaned_files_removed": 0,
|
|
"bytes_freed": 0,
|
|
"errors": []
|
|
}
|
|
|
|
# Clean up temp directory
|
|
temp_dirs = ["/tmp", "/app/temp", f"{settings.UPLOAD_PATH}/temp"]
|
|
|
|
for temp_dir in temp_dirs:
|
|
if os.path.exists(temp_dir):
|
|
for filename in os.listdir(temp_dir):
|
|
filepath = os.path.join(temp_dir, filename)
|
|
if os.path.isfile(filepath):
|
|
try:
|
|
file_mtime = datetime.fromtimestamp(os.path.getmtime(filepath))
|
|
if file_mtime < cutoff_date:
|
|
file_size = os.path.getsize(filepath)
|
|
os.remove(filepath)
|
|
cleanup_results["temp_files_removed"] += 1
|
|
cleanup_results["bytes_freed"] += file_size
|
|
except Exception as e:
|
|
cleanup_results["errors"].append(f"Failed to remove temp file {filepath}: {str(e)}")
|
|
|
|
# TODO: Check for orphaned files in uploads directory
|
|
# - Query database for all attachment file_paths
|
|
# - Compare with actual files in upload directory
|
|
# - Remove orphaned files
|
|
|
|
task_logger.info(
|
|
"Temp files cleanup completed",
|
|
temp_files_removed=cleanup_results["temp_files_removed"],
|
|
orphaned_files_removed=cleanup_results["orphaned_files_removed"],
|
|
bytes_freed=cleanup_results["bytes_freed"]
|
|
)
|
|
|
|
return cleanup_results
|
|
|
|
except Exception as exc:
|
|
task_logger.error(
|
|
"Temp files cleanup failed",
|
|
error=str(exc),
|
|
exc_info=True
|
|
)
|
|
raise
|
|
|
|
|
|
@celery_app.task(
|
|
bind=True,
|
|
time_limit=300,
|
|
soft_time_limit=240
|
|
)
|
|
def database_maintenance(self) -> Dict[str, Any]:
|
|
"""
|
|
Perform database maintenance tasks.
|
|
|
|
- VACUUM and ANALYZE tables
|
|
- Update statistics
|
|
- Check for slow queries
|
|
- Optimize indices if needed
|
|
"""
|
|
task_logger = logger.bind(
|
|
task_id=self.request.id,
|
|
task_name=self.name
|
|
)
|
|
|
|
task_logger.info("Starting database maintenance")
|
|
|
|
try:
|
|
# TODO: Implement database maintenance
|
|
# For now, return placeholder results
|
|
|
|
maintenance_results = {
|
|
"started_at": datetime.utcnow().isoformat(),
|
|
"tables_analyzed": 0,
|
|
"indices_optimized": 0,
|
|
"slow_queries_found": 0,
|
|
"space_reclaimed_mb": 0
|
|
}
|
|
|
|
task_logger.info("Database maintenance completed (placeholder)")
|
|
|
|
return maintenance_results
|
|
|
|
except Exception as exc:
|
|
task_logger.error(
|
|
"Database maintenance failed",
|
|
error=str(exc),
|
|
exc_info=True
|
|
)
|
|
raise |