Files
Click/services/visit_service.py
T
Yuriy Yuriev f262616aad fix click
2026-06-05 20:56:26 +07:00

121 lines
3.9 KiB
Python

"""Service for scheduled page visits."""
import asyncio
import random
import logging
from typing import Optional, Callable, Awaitable
from config.settings import settings
from services.browser_service import BrowserService
logger = logging.getLogger(__name__)
class VisitScheduler:
"""
Scheduler for periodic page visits.
Features:
- Schedule periodic visits
- Configurable delays and reading times
- Progress tracking
"""
def __init__(
self,
browser_service: BrowserService,
url: str,
min_delay: int = None,
max_delay: int = None,
min_reading: int = None,
max_reading: int = None,
max_visits: Optional[int] = None,
on_visit_complete: Optional[Callable] = None,
on_progress: Optional[Callable] = None
):
self.browser_service = browser_service
self.url = url
self.min_delay = min_delay or settings.DEFAULT_MIN_DELAY
self.max_delay = max_delay or settings.DEFAULT_MAX_DELAY
self.min_reading = min_reading or settings.DEFAULT_MIN_READING
self.max_reading = max_reading or settings.DEFAULT_MAX_READING
self.max_visits = max_visits
self.on_visit_complete = on_visit_complete
self.on_progress = on_progress
self.visit_count = 0
self.successful = 0
self.failed = 0
async def run(self) -> dict:
"""
Run scheduled visits.
Returns:
Statistics dict
"""
total_reading_time = 0
loop = asyncio.get_event_loop()
try:
while True:
if self.max_visits and self.visit_count >= self.max_visits:
logger.info(f"Visit limit reached: {self.max_visits}")
break
# Randomize parameters once per cycle
current_reading = random.randint(self.min_reading, self.max_reading)
current_delay = random.randint(self.min_delay, self.max_delay)
self.visit_count += 1
# Record start time so the next visit fires strictly on the timer
tick_start = loop.time()
logger.info(
f"Visit {self.visit_count}/{'' if not self.max_visits else self.max_visits}: "
f"reading={current_reading}s delay={current_delay}s"
)
# Visit page
result = await self.browser_service.visit_page(
self.url, current_reading
)
if result.success:
self.successful += 1
total_reading_time += current_reading
else:
self.failed += 1
# Notify about visit completion
if self.on_visit_complete:
await self.on_visit_complete(
self.visit_count, result, current_delay, current_reading
)
if self.max_visits and self.visit_count >= self.max_visits:
break
# Wait only the remaining time so that the period between visit
# starts is exactly current_delay (independent of visit duration)
elapsed = loop.time() - tick_start
remaining = current_delay - elapsed
if self.on_progress:
await self.on_progress(self.visit_count, current_delay)
if remaining > 0:
await asyncio.sleep(remaining)
except asyncio.CancelledError:
logger.info("Visit scheduler cancelled")
raise
return {
"total_visits": self.visit_count,
"successful": self.successful,
"failed": self.failed,
"avg_reading_time": total_reading_time / self.successful if self.successful else 0
}