amo-server/servers/job_server.py

205 lines
6.1 KiB
Python

"""
Job server for managing background job publishing and status tracking.
This module provides the JobServer class for queuing various types of jobs
using FastStream Redis broker and tracking their status in the database.
"""
import uuid
import logging
from datetime import datetime
from typing import Dict, Any, Optional
from faststream.redis import RedisBroker
from adapters.sqlite.database import get_db
logger = logging.getLogger(__name__)
class JobServer:
"""Server for managing background job operations."""
def __init__(self, broker: RedisBroker):
"""
Initialize JobServer with Redis broker.
Args:
broker: FastStream Redis broker instance
"""
self.broker = broker
self.db = get_db
async def queue_export_job(self, configuration_id: int) -> str:
"""
Queue an export job for processing.
Args:
configuration_id: ID of the export configuration to process
Returns:
job_id: Unique identifier for the queued job
"""
job_id = str(uuid.uuid4())
job_data = {
"job_id": job_id,
"job_type": "export",
"configuration_id": configuration_id,
"created_at": datetime.utcnow().isoformat(),
"status": "queued"
}
try:
# Create job record in database
await self._create_job_record(job_data)
# Publish to export-jobs channel
await self.broker.publish(job_data, "export-jobs")
logger.info(f"Export job {job_id} queued successfully")
return job_id
except Exception as e:
logger.error(f"Failed to queue export job: {str(e)}")
await self._update_job_status(job_id, "failed", error_message=str(e))
raise
async def queue_sync_job(
self,
entity_type: str,
limit: Optional[int] = None,
page: Optional[int] = None,
**kwargs
) -> str:
"""
Queue a data synchronization job.
Args:
entity_type: Type of entity to sync (deals, contacts, companies, etc.)
limit: Maximum number of records to sync
page: Page number for pagination
**kwargs: Additional parameters for the sync job
Returns:
job_id: Unique identifier for the queued job
"""
job_id = str(uuid.uuid4())
sync_data = {
"job_id": job_id,
"job_type": "sync",
"entity_type": entity_type,
"parameters": {
"limit": limit,
"page": page,
**kwargs
},
"created_at": datetime.utcnow().isoformat(),
"status": "queued"
}
try:
# Create job record in database
await self._create_job_record(sync_data)
# Publish to sync-jobs channel
await self.broker.publish(sync_data, "sync-jobs")
logger.info(f"Sync job {job_id} for {entity_type} queued successfully")
return job_id
except Exception as e:
logger.error(f"Failed to queue sync job: {str(e)}")
await self._update_job_status(job_id, "failed", error_message=str(e))
raise
async def schedule_refresh_job(self, entity_type: str) -> None:
"""
Schedule periodic data refresh for an entity type.
Args:
entity_type: Type of entity to refresh
"""
try:
# Publish to refresh-jobs channel
await self.broker.publish(entity_type, "refresh-jobs")
logger.info(f"Refresh job for {entity_type} scheduled successfully")
except Exception as e:
logger.error(f"Failed to schedule refresh job for {entity_type}: {str(e)}")
raise
async def get_job_status(self, job_id: str) -> Optional[Dict[str, Any]]:
"""
Get the status of a specific job.
Args:
job_id: Unique identifier of the job
Returns:
Job status information or None if not found
"""
try:
# TODO: Implement database query to get job status
# This would query the export_jobs table
pass
except Exception as e:
logger.error(f"Failed to get job status for {job_id}: {str(e)}")
return None
async def list_jobs(
self,
status: Optional[str] = None,
job_type: Optional[str] = None,
limit: int = 20,
offset: int = 0
) -> Dict[str, Any]:
"""
List jobs with optional filtering.
Args:
status: Filter by job status (pending, running, completed, failed)
job_type: Filter by job type (export, sync)
limit: Maximum number of jobs to return
offset: Number of jobs to skip
Returns:
Dictionary containing jobs list and pagination info
"""
try:
# TODO: Implement database query to list jobs
# This would query the export_jobs table with filters
pass
except Exception as e:
logger.error(f"Failed to list jobs: {str(e)}")
return {"jobs": [], "total": 0}
async def _create_job_record(self, job_data: Dict[str, Any]) -> None:
"""
Create a job record in the database.
Args:
job_data: Job information to store
"""
# TODO: Implement database insertion
# This would insert into the export_jobs table
pass
async def _update_job_status(
self,
job_id: str,
status: str,
error_message: Optional[str] = None,
**kwargs
) -> None:
"""
Update job status in the database.
Args:
job_id: Unique identifier of the job
status: New status (pending, running, completed, failed)
error_message: Error message if status is failed
**kwargs: Additional fields to update
"""
# TODO: Implement database update
# This would update the export_jobs table
pass