Skip to content

Commit

Permalink
Copy code from cla_callmeback with unittests
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Driver committed Oct 24, 2014
1 parent 5295594 commit f686231
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 0 deletions.
136 changes: 136 additions & 0 deletions cla_common/call_centre_availability/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import datetime
import requests

from django.core.cache import cache


BANK_HOLIDAYS_URL = 'https://www.gov.uk/bank-holidays/england-and-wales.json'


def current_datetime():
# this function is to make unit testing simpler
return datetime.datetime.now()


def in_the_past(time):
return not time > current_datetime()


def before_9am(time):
return not time.time() >= datetime.time(9, 0)


def after_8pm(time):
return not time.time() < datetime.time(20, 0)


def on_sunday(time):
return time.weekday() == 6


def parse_date(text):
return datetime.datetime.strptime(text, '%Y-%m-%d')


def get_date(bank_holiday):
return parse_date(bank_holiday['date'])


def load_bank_holidays():
holidays = requests.get(BANK_HOLIDAYS_URL).json()['events']
return map(get_date, holidays)


def cache_bank_holidays(bank_holidays):
one_year = 365 * 24 * 60 * 60
cache.set('bank_holidays', bank_holidays, one_year)


def bank_holidays():
bank_holidays = cache.get('bank_holidays')
if not bank_holidays:
bank_holidays = load_bank_holidays()
cache_bank_holidays(bank_holidays)
return bank_holidays


def on_bank_holiday(time):
day = datetime.datetime.combine(time.date(), datetime.time())
return day in bank_holidays()


def on_saturday(time):
return time.weekday() == 5


def after_1230(time):
return not time.time() < datetime.time(12, 30)


def is_today(time):
return time.date() == current_datetime().date()


def too_late(time):
one_hour = datetime.timedelta(minutes=60)
now = current_datetime()
return time.time() <= (now + one_hour).time()


def available(time):
return not (
in_the_past(time) or
(before_9am(time) or after_8pm(time)) or
on_sunday(time) or
on_bank_holiday(time) or
(on_saturday(time) and after_1230(time)) or
(is_today(time) and too_late(time)))


def available_days(num):
nine_am = datetime.time(9)
today = datetime.datetime.combine(current_datetime().date(), nine_am)
one_day = datetime.timedelta(days=1)
while num:
if available(today):
value = date_format(today, 'Ymd')
display = date_format(today, 'l jS')
yield (value, display)
num -= 1
today = today + one_day


def every_interval(time, end, mins=15):
interval = datetime.timedelta(minutes=mins)
while time < end:
yield time
time += interval


def time_slots(day=None):
if not day:
day = datetime.date(1, 1, 1) # Monday

start = datetime.datetime.combine(day, datetime.time(9))
end = datetime.datetime.combine(day, datetime.time(20))

return list(filter(available, every_interval(start, end=end, mins=15)))


def today_slots(*args):
return time_slots(current_datetime().date())


def tomorrow_slots(*args):
tomorrow = current_datetime() + datetime.timedelta(days=1)
return time_slots(tomorrow.date())


def time_choices(times):

def time_choice(time):
return (
time_format(time, 'Hi'),
time_format(time, 'g:i A'))

return map(time_choice, times)
90 changes: 90 additions & 0 deletions cla_common/tests/test_call_centre_availability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from datetime import datetime
import unittest
from contextlib import contextmanager

from .. import call_centre_availability
from ..call_centre_availability import available


class MonkeyPatch(object):

def __init__(self, obj, attr, value):
self.obj = obj
self.attr = attr
self.applied = False

if hasattr(obj, attr):
self.original = getattr(obj, attr)
setattr(obj, attr, value)
self.applied = True

def undo(self):
if self.applied:
setattr(self.obj, self.attr, self.original)


@contextmanager
def override_current_time(dt):
override = lambda: dt
patch = MonkeyPatch(call_centre_availability, 'current_datetime', override)
yield
patch.undo()


def mock_bank_holidays():
return [datetime(2014, 12, 25, 0, 0)]


def pretty(time):
return '{0:%a, %d %b %I:%M %p}'.format(time)


class CallCentreAvailabilityTestCase(unittest.TestCase):

def setUp(self):
self.bank_holiday_patch = MonkeyPatch(
call_centre_availability,
'bank_holidays',
mock_bank_holidays)
self.now = datetime(2014, 10, 22, 9, 0)

def tearDown(self):
self.bank_holiday_patch.undo()

def assertAvailable(self, time):
fail_msg = '{0} is not available at {1}'.format(
pretty(time),
pretty(self.now))
with override_current_time(self.now):
self.assertTrue(available(time), fail_msg)

def assertNotAvailable(self, time):
fail_msg = '{0} is available at {1}'.format(
pretty(time),
pretty(self.now))
with override_current_time(self.now):
self.assertFalse(available(time), fail_msg)

def test_weekday_9am(self):
self.assertAvailable(datetime(2014, 10, 23, 9, 0))

def test_weekday_before_9am(self):
self.assertNotAvailable(datetime(2014, 10, 23, 7, 0))

def test_weekday_after_8pm(self):
self.assertNotAvailable(datetime(2014, 10, 23, 21, 0))

def test_saturday_after_1230pm(self):
self.assertAvailable(datetime(2014, 10, 25, 12, 15))
self.assertNotAvailable(datetime(2014, 10, 25, 12, 30))

def test_sunday(self):
self.assertNotAvailable(datetime(2014, 10, 26, 9, 0))

def test_today_within_1hr(self):
self.now = datetime(2014, 10, 23, 12, 0)
self.assertNotAvailable(datetime(2014, 10, 23, 13, 0))
self.assertAvailable(datetime(2014, 10, 23, 13, 15))

def test_bank_holiday(self):
self.assertNotAvailable(datetime(2014, 12, 25, 9, 0))

0 comments on commit f686231

Please sign in to comment.