Skip to content

Commit

Permalink
Adding a SleepSchedule worker. Pause for some time every day (#2193)
Browse files Browse the repository at this point in the history
* Added Sleeper worker

* changed Sleep worker name to SleepSchedule

* fixed wrong import names

* changed name in log
  • Loading branch information
johnbuluba authored and TheSavior committed Aug 1, 2016
1 parent b35260d commit 589ada8
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 1 deletion.
3 changes: 2 additions & 1 deletion pokemongo_bot/cell_workers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
from follow_spiral import FollowSpiral
from collect_level_up_reward import CollectLevelUpReward
from base_task import BaseTask
from follow_cluster import FollowCluster
from follow_cluster import FollowCluster
from sleep_schedule import SleepSchedule
98 changes: 98 additions & 0 deletions pokemongo_bot/cell_workers/sleep_schedule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from datetime import datetime, timedelta
from time import sleep
from random import uniform
from pokemongo_bot import logger
from pokemongo_bot.cell_workers.base_task import BaseTask


class SleepSchedule(BaseTask):
"""Pauses the execution of the bot every day for some time
Simulates the user going to sleep every day for some time, the sleep time
and the duration is changed every day by a random offset defined in the
config file
Example Config:
{
"type": "SleepSchedule",
"config": {
"time": "12:00",
"duration":"5:30",
"time_random_offset": "00:30",
"duration_random_offset": "00:30"
}
}
time: (HH:MM) local time that the bot should sleep
duration: (HH:MM) the duration of sleep
time_random_offset: (HH:MM) random offset of time that the sleep will start
for this example the possible start time is 11:30-12:30
duration_random_offset: (HH:MM) random offset of duration of sleep
for this example the possible duration is 5:00-6:00
"""

LOG_INTERVAL_SECONDS = 600
SCHEDULING_MARGIN = timedelta(minutes=10) # Skip if next sleep is RESCHEDULING_MARGIN from now

def initialize(self):
# self.bot.event_manager.register_event('sleeper_scheduled', parameters=('datetime',))
self._process_config()
self._schedule_next_sleep()

def work(self):
if datetime.now() >= self._next_sleep:
self._sleep()
self._schedule_next_sleep()
self.bot.login()

def _process_config(self):
self.time = datetime.strptime(self.config.get('time', '01:00'), '%H:%M')

# Using datetime for easier stripping of timedeltas
duration = datetime.strptime(self.config.get('duration', '07:00'), '%H:%M')
self.duration = int(timedelta(hours=duration.hour, minutes=duration.minute).total_seconds())

time_random_offset = datetime.strptime(self.config.get('time_random_offset', '01:00'), '%H:%M')
self.time_random_offset = int(
timedelta(
hours=time_random_offset.hour, minutes=time_random_offset.minute).total_seconds())

duration_random_offset = datetime.strptime(self.config.get('duration_random_offset', '00:30'), '%H:%M')
self.duration_random_offset = int(
timedelta(
hours=duration_random_offset.hour, minutes=duration_random_offset.minute).total_seconds())

def _schedule_next_sleep(self):
self._next_sleep = self._get_next_sleep_schedule()
self._next_duration = self._get_next_duration()
logger.log('SleepSchedule: next sleep at {}'.format(str(self._next_sleep)), color='green')

def _get_next_sleep_schedule(self):
now = datetime.now() + self.SCHEDULING_MARGIN
next_time = now.replace(hour=self.time.hour, minute=self.time.minute)

next_time += timedelta(seconds=self._get_random_offset(self.time_random_offset))

# If sleep time is passed add one day
if next_time <= now:
next_time += timedelta(days=1)

return next_time

def _get_next_duration(self):
duration = self.duration + self._get_random_offset(self.duration_random_offset)
return duration

def _get_random_offset(self, max_offset):
offset = uniform(-max_offset, max_offset)
return int(offset)

def _sleep(self):
sleep_to_go = self._next_duration
logger.log('It\'s time for sleep.')
while sleep_to_go > 0:
logger.log('Sleeping for {} more seconds'.format(sleep_to_go), 'yellow')
if sleep_to_go < self.LOG_INTERVAL_SECONDS:
sleep(sleep_to_go)
sleep_to_go = 0
else:
sleep(self.LOG_INTERVAL_SECONDS)
sleep_to_go -= self.LOG_INTERVAL_SECONDS
107 changes: 107 additions & 0 deletions pokemongo_bot/test/sleep_schedule_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import unittest
from datetime import timedelta, datetime
from mock import patch, MagicMock
from pokemongo_bot.cell_workers.sleep_schedule import SleepSchedule
from tests import FakeBot


class SleepScheculeTestCase(unittest.TestCase):
config = {'time': '12:20', 'duration': '01:05', 'time_random_offset': '00:05', 'duration_random_offset': '00:05'}

def setUp(self):
self.bot = FakeBot()
self.worker = SleepSchedule(self.bot, self.config)

def test_config(self):
self.assertEqual(self.worker.time.hour, 12)
self.assertEqual(self.worker.time.minute, 20)
self.assertEqual(self.worker.duration, timedelta(hours=1, minutes=5).total_seconds())
self.assertEqual(self.worker.time_random_offset, timedelta(minutes=5).total_seconds())
self.assertEqual(self.worker.duration_random_offset, timedelta(minutes=5).total_seconds())

@patch('pokemongo_bot.cell_workers.sleep_schedule.datetime')
def test_get_next_time(self, mock_datetime):
mock_datetime.now.return_value = datetime(year=2016, month=8, day=01, hour=8, minute=0)

next_time = self.worker._get_next_sleep_schedule()
from_date = datetime(year=2016, month=8, day=1, hour=12, minute=15)
to_date = datetime(year=2016, month=8, day=1, hour=12, minute=25)

self.assertGreaterEqual(next_time, from_date)
self.assertLessEqual(next_time, to_date)

@patch('pokemongo_bot.cell_workers.sleep_schedule.datetime')
def test_get_next_time_called_near_activation_time(self, mock_datetime):
mock_datetime.now.return_value = datetime(year=2016, month=8, day=1, hour=12, minute=25)

next = self.worker._get_next_sleep_schedule()
from_date = datetime(year=2016, month=8, day=02, hour=12, minute=15)
to_date = datetime(year=2016, month=8, day=02, hour=12, minute=25)

self.assertGreaterEqual(next, from_date)
self.assertLessEqual(next, to_date)

@patch('pokemongo_bot.cell_workers.sleep_schedule.datetime')
def test_get_next_time_called_when_this_days_time_passed(self, mock_datetime):
mock_datetime.now.return_value = datetime(year=2016, month=8, day=1, hour=14, minute=0)

next = self.worker._get_next_sleep_schedule()
from_date = datetime(year=2016, month=8, day=02, hour=12, minute=15)
to_date = datetime(year=2016, month=8, day=02, hour=12, minute=25)

self.assertGreaterEqual(next, from_date)
self.assertLessEqual(next, to_date)

def test_get_next_duration(self):
from_seconds = int(timedelta(hours=1).total_seconds())
to_seconds = int(timedelta(hours=1, minutes=10).total_seconds())

duration = self.worker._get_next_duration()

self.assertGreaterEqual(duration, from_seconds)
self.assertLessEqual(duration, to_seconds)

@patch('pokemongo_bot.cell_workers.sleep_schedule.sleep')
def test_sleep(self, mock_sleep):
self.worker._next_duration = SleepSchedule.LOG_INTERVAL_SECONDS * 10
self.worker._sleep()
#Sleep should be called 10 times with LOG_INTERVAL_SECONDS as argument
self.assertEqual(mock_sleep.call_count, 10)
calls = [x[0][0] for x in mock_sleep.call_args_list]
for arg in calls:
self.assertEqual(arg, SleepSchedule.LOG_INTERVAL_SECONDS)

@patch('pokemongo_bot.cell_workers.sleep_schedule.sleep')
def test_sleep_not_divedable_by_interval(self, mock_sleep):
self.worker._next_duration = SleepSchedule.LOG_INTERVAL_SECONDS * 10 + 5
self.worker._sleep()
self.assertEqual(mock_sleep.call_count, 11)

calls = [x[0][0] for x in mock_sleep.call_args_list]
for arg in calls[:-1]:
self.assertEqual(arg, SleepSchedule.LOG_INTERVAL_SECONDS)
#Last call must be 5
self.assertEqual(calls[-1], 5)

@patch('pokemongo_bot.cell_workers.sleep_schedule.sleep')
@patch('pokemongo_bot.cell_workers.sleep_schedule.datetime')
def test_call_work_before_schedule(self, mock_datetime, mock_sleep):
self.worker._next_sleep = datetime(year=2016, month=8, day=1, hour=12, minute=0)
mock_datetime.now.return_value = self.worker._next_sleep - timedelta(minutes=5)

self.worker.work()

self.assertEqual(mock_sleep.call_count, 0)

@patch('pokemongo_bot.cell_workers.sleep_schedule.sleep')
@patch('pokemongo_bot.cell_workers.sleep_schedule.datetime')
def test_call_work_after_schedule(self, mock_datetime, mock_sleep):
self.bot.login = MagicMock()
self.worker._next_sleep = datetime(year=2016, month=8, day=1, hour=12, minute=0)
# Change time to be after schedule
mock_datetime.now.return_value = self.worker._next_sleep + timedelta(minutes=5)

self.worker.work()

self.assertGreater(mock_sleep.call_count, 0)
self.assertGreater(self.bot.login.call_count, 0)

0 comments on commit 589ada8

Please sign in to comment.