- Implemented tests for ScheduledTaskRepository covering task creation, retrieval, filtering, and status updates. - Developed tests for SchedulerService including task creation, cancellation, user task retrieval, and maintenance jobs. - Created tests for TaskHandlerRegistry to validate task execution for various types, including credit recharge and sound playback. - Ensured proper error handling and edge cases in task execution scenarios. - Added fixtures and mocks to facilitate isolated testing of services and repositories.
125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
"""Scheduled task model for flexible task scheduling with timezone support."""
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import Any, Optional
|
|
|
|
from sqlmodel import JSON, Column, Field, SQLModel
|
|
|
|
from app.models.base import BaseModel
|
|
|
|
|
|
class TaskType(str, Enum):
|
|
"""Available task types."""
|
|
|
|
CREDIT_RECHARGE = "credit_recharge"
|
|
PLAY_SOUND = "play_sound"
|
|
PLAY_PLAYLIST = "play_playlist"
|
|
|
|
|
|
class TaskStatus(str, Enum):
|
|
"""Task execution status."""
|
|
|
|
PENDING = "pending"
|
|
RUNNING = "running"
|
|
COMPLETED = "completed"
|
|
FAILED = "failed"
|
|
CANCELLED = "cancelled"
|
|
|
|
|
|
class RecurrenceType(str, Enum):
|
|
"""Recurrence patterns."""
|
|
|
|
NONE = "none" # One-shot task
|
|
HOURLY = "hourly"
|
|
DAILY = "daily"
|
|
WEEKLY = "weekly"
|
|
MONTHLY = "monthly"
|
|
YEARLY = "yearly"
|
|
CRON = "cron" # Custom cron expression
|
|
|
|
|
|
class ScheduledTask(BaseModel, table=True):
|
|
"""Model for scheduled tasks with timezone support."""
|
|
|
|
__tablename__ = "scheduled_tasks"
|
|
|
|
id: int | None = Field(primary_key=True, default=None)
|
|
name: str = Field(max_length=255, description="Human-readable task name")
|
|
task_type: TaskType = Field(description="Type of task to execute")
|
|
status: TaskStatus = Field(default=TaskStatus.PENDING)
|
|
|
|
# Scheduling fields with timezone support
|
|
scheduled_at: datetime = Field(description="When the task should be executed (UTC)")
|
|
timezone: str = Field(
|
|
default="UTC",
|
|
description="Timezone for scheduling (e.g., 'America/New_York', 'Europe/Paris')",
|
|
)
|
|
recurrence_type: RecurrenceType = Field(default=RecurrenceType.NONE)
|
|
cron_expression: Optional[str] = Field(
|
|
default=None,
|
|
description="Cron expression for custom recurrence (when recurrence_type is CRON)",
|
|
)
|
|
recurrence_count: Optional[int] = Field(
|
|
default=None,
|
|
description="Number of times to repeat (None for infinite)",
|
|
)
|
|
executions_count: int = Field(default=0, description="Number of times executed")
|
|
|
|
# Task parameters
|
|
parameters: dict[str, Any] = Field(
|
|
default_factory=dict,
|
|
sa_column=Column(JSON),
|
|
description="Task-specific parameters",
|
|
)
|
|
|
|
# User association (None for system tasks)
|
|
user_id: Optional[int] = Field(
|
|
default=None,
|
|
foreign_key="user.id",
|
|
description="User who created the task (None for system tasks)",
|
|
)
|
|
|
|
# Execution tracking
|
|
last_executed_at: Optional[datetime] = Field(
|
|
default=None,
|
|
description="When the task was last executed (UTC)",
|
|
)
|
|
next_execution_at: Optional[datetime] = Field(
|
|
default=None,
|
|
description="When the task should be executed next (UTC, for recurring tasks)",
|
|
)
|
|
error_message: Optional[str] = Field(
|
|
default=None,
|
|
description="Error message if execution failed",
|
|
)
|
|
|
|
# Task lifecycle
|
|
is_active: bool = Field(default=True, description="Whether the task is active")
|
|
expires_at: Optional[datetime] = Field(
|
|
default=None,
|
|
description="When the task expires (UTC, optional)",
|
|
)
|
|
|
|
def is_expired(self) -> bool:
|
|
"""Check if the task has expired."""
|
|
if self.expires_at is None:
|
|
return False
|
|
return datetime.utcnow() > self.expires_at
|
|
|
|
def is_recurring(self) -> bool:
|
|
"""Check if the task is recurring."""
|
|
return self.recurrence_type != RecurrenceType.NONE
|
|
|
|
def should_repeat(self) -> bool:
|
|
"""Check if the task should be repeated."""
|
|
if not self.is_recurring():
|
|
return False
|
|
if self.recurrence_count is None:
|
|
return True
|
|
return self.executions_count < self.recurrence_count
|
|
|
|
def is_system_task(self) -> bool:
|
|
"""Check if this is a system task (no user association)."""
|
|
return self.user_id is None |