reminder-bot/bot/services/reminders_service.py

309 lines
8.0 KiB
Python

"""Reminder business logic service."""
from datetime import datetime, time
from typing import List, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from bot.db.models import Reminder
from bot.db.operations import (
create_reminder,
get_reminder_by_id,
get_user_reminders,
update_reminder,
delete_reminder,
mark_reminder_done,
snooze_reminder,
toggle_reminder_active,
)
from bot.services.time_service import get_time_service
from bot.logging_config import get_logger
logger = get_logger(__name__)
class RemindersService:
"""Service for reminder-related operations."""
def __init__(self):
"""Initialize RemindersService."""
self.time_service = get_time_service()
async def create_new_reminder(
self,
session: AsyncSession,
user_id: int,
text: str,
days_interval: int,
time_of_day: time,
) -> Reminder:
"""
Create a new reminder.
Args:
session: Database session
user_id: User's database ID
text: Reminder text
days_interval: Days between reminders
time_of_day: Time of day for reminder
Returns:
Created Reminder instance
"""
# Calculate next run time
next_run_at = self.time_service.calculate_next_run_time(
time_of_day=time_of_day,
days_interval=days_interval,
)
# Create reminder
reminder = await create_reminder(
session=session,
user_id=user_id,
text=text,
days_interval=days_interval,
time_of_day=time_of_day,
next_run_at=next_run_at,
)
logger.info(
f"Created reminder {reminder.id} for user {user_id}, "
f"next run at {next_run_at}"
)
return reminder
async def get_reminder(
self,
session: AsyncSession,
reminder_id: int,
) -> Optional[Reminder]:
"""
Get reminder by ID.
Args:
session: Database session
reminder_id: Reminder ID
Returns:
Reminder instance or None
"""
return await get_reminder_by_id(session, reminder_id)
async def get_user_all_reminders(
self,
session: AsyncSession,
user_id: int,
active_only: bool = False,
) -> List[Reminder]:
"""
Get all reminders for a user.
Args:
session: Database session
user_id: User's database ID
active_only: Return only active reminders
Returns:
List of Reminder instances
"""
return await get_user_reminders(session, user_id, active_only)
async def update_reminder_text(
self,
session: AsyncSession,
reminder_id: int,
new_text: str,
) -> Optional[Reminder]:
"""
Update reminder text.
Args:
session: Database session
reminder_id: Reminder ID
new_text: New reminder text
Returns:
Updated Reminder instance or None
"""
return await update_reminder(session, reminder_id, text=new_text)
async def update_reminder_interval(
self,
session: AsyncSession,
reminder_id: int,
new_days_interval: int,
) -> Optional[Reminder]:
"""
Update reminder interval.
Args:
session: Database session
reminder_id: Reminder ID
new_days_interval: New days interval
Returns:
Updated Reminder instance or None
"""
reminder = await get_reminder_by_id(session, reminder_id)
if not reminder:
return None
# Recalculate next_run_at with new interval
next_run_at = self.time_service.calculate_next_run_time(
time_of_day=reminder.time_of_day,
days_interval=new_days_interval,
)
return await update_reminder(
session,
reminder_id,
days_interval=new_days_interval,
next_run_at=next_run_at,
)
async def update_reminder_time(
self,
session: AsyncSession,
reminder_id: int,
new_time_of_day: time,
) -> Optional[Reminder]:
"""
Update reminder time.
Args:
session: Database session
reminder_id: Reminder ID
new_time_of_day: New time of day
Returns:
Updated Reminder instance or None
"""
reminder = await get_reminder_by_id(session, reminder_id)
if not reminder:
return None
# Recalculate next_run_at with new time
next_run_at = self.time_service.calculate_next_run_time(
time_of_day=new_time_of_day,
days_interval=reminder.days_interval,
)
return await update_reminder(
session,
reminder_id,
time_of_day=new_time_of_day,
next_run_at=next_run_at,
)
async def delete_reminder_by_id(
self,
session: AsyncSession,
reminder_id: int,
) -> bool:
"""
Delete a reminder.
Args:
session: Database session
reminder_id: Reminder ID
Returns:
True if deleted, False if not found
"""
return await delete_reminder(session, reminder_id)
async def mark_as_done(
self,
session: AsyncSession,
reminder_id: int,
) -> Optional[Reminder]:
"""
Mark reminder as done and schedule next occurrence.
Args:
session: Database session
reminder_id: Reminder ID
Returns:
Updated Reminder instance or None
"""
reminder = await get_reminder_by_id(session, reminder_id)
if not reminder:
return None
# Calculate next occurrence
next_run_at = self.time_service.calculate_next_occurrence(
current_run=reminder.next_run_at,
days_interval=reminder.days_interval,
)
return await mark_reminder_done(session, reminder_id, next_run_at)
async def snooze(
self,
session: AsyncSession,
reminder_id: int,
hours: int,
) -> Optional[Reminder]:
"""
Snooze reminder for specified hours.
Args:
session: Database session
reminder_id: Reminder ID
hours: Hours to snooze
Returns:
Updated Reminder instance or None
"""
now = self.time_service.get_now()
next_run_at = self.time_service.add_hours(now, hours)
return await snooze_reminder(session, reminder_id, next_run_at)
async def pause_reminder(
self,
session: AsyncSession,
reminder_id: int,
) -> Optional[Reminder]:
"""
Pause (deactivate) a reminder.
Args:
session: Database session
reminder_id: Reminder ID
Returns:
Updated Reminder instance or None
"""
return await toggle_reminder_active(session, reminder_id, is_active=False)
async def resume_reminder(
self,
session: AsyncSession,
reminder_id: int,
) -> Optional[Reminder]:
"""
Resume (activate) a reminder.
Args:
session: Database session
reminder_id: Reminder ID
Returns:
Updated Reminder instance or None
"""
reminder = await get_reminder_by_id(session, reminder_id)
if not reminder:
return None
# Recalculate next_run_at from now
next_run_at = self.time_service.calculate_next_run_time(
time_of_day=reminder.time_of_day,
days_interval=reminder.days_interval,
)
# Update and activate
await update_reminder(session, reminder_id, next_run_at=next_run_at)
return await toggle_reminder_active(session, reminder_id, is_active=True)