309 lines
8.0 KiB
Python
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)
|