diff --git a/my/calendar/__init__.py b/my/calendar/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/my/calendar/holidays.py b/my/calendar/holidays.py index 4f45a93..8e0c206 100644 --- a/my/calendar/holidays.py +++ b/my/calendar/holidays.py @@ -1,91 +1,49 @@ """ -Public holidays (automatic) and days off work (manual inputs) +Holidays and days off work """ +REQUIRES = [ + 'workalendar', # library to determine public holidays +] -from functools import lru_cache from datetime import date, datetime, timedelta -import re -from typing import Tuple, Iterator, List, Union +from typing import Union -from my.config.holidays_data import HOLIDAYS_DATA - - -# pip3 install workalendar +# TODO would be nice to do it dynamically depending on the past timezones... from workalendar.europe import UnitedKingdom # type: ignore -cal = UnitedKingdom() # TODO +cal = UnitedKingdom() # TODO that should depend on country/'location' of residence I suppose? -Dateish = Union[datetime, date, str] - - -def as_date(dd: Dateish) -> date: +# todo move to common? +DateIsh = Union[datetime, date, str] +def as_date(dd: DateIsh) -> date: if isinstance(dd, datetime): return dd.date() elif isinstance(dd, date): return dd else: + # todo parse isoformat?? return as_date(datetime.strptime(dd, '%Y%m%d')) -@lru_cache(1) -def get_days_off_work() -> List[date]: - return list(iter_days_off_work()) - - -def is_day_off_work(d: date) -> bool: - return d in get_days_off_work() - - -def is_working_day(d: Dateish) -> bool: +def is_holiday(d: DateIsh) -> bool: day = as_date(d) - if not cal.is_working_day(day): - # public holiday -- def holiday - return False - # otherwise rely on work data - return not is_day_off_work(day) + return not cal.is_working_day(day) -def is_holiday(d: Dateish) -> bool: - return not(is_working_day(d)) +def is_workday(d: DateIsh) -> bool: + return not is_holiday(d) -def _iter_work_data() -> Iterator[Tuple[date, int]]: - emitted = 0 - for x in HOLIDAYS_DATA.splitlines(): - m = re.search(r'(\d\d/\d\d/\d\d\d\d)(.*)-(\d+.\d+) days \d+.\d+ days', x) - if m is None: - continue - (ds, cmnt, dayss) = m.groups() - if 'carry over' in cmnt: - continue - - d = datetime.strptime(ds, '%d/%m/%Y').date() - dd, u = dayss.split('.') - assert u == '00' # TODO meh - - yield d, int(dd) - emitted += 1 - assert emitted > 5 # arbitrary, just a sanity check.. (TODO move to tests?) - - -def iter_days_off_work() -> Iterator[date]: - for d, span in _iter_work_data(): - dd = d - while span > 0: - # only count it if it wasnt' a public holiday/weekend already - if cal.is_working_day(dd): - yield dd - span -= 1 - dd += timedelta(days=1) - - -def test(): - assert is_holiday('20190101') - assert not is_holiday('20180601') - - -if __name__ == '__main__': - for d in iter_days_off_work(): - print(d, ' | ', d.strftime('%d %b')) +from ..core.common import Stats +def stats() -> Stats: + # meh, but not sure what would be a better test? + res = {} + year = datetime.now().year + jan1 = date(year=year, month=1, day=1) + for x in range(-7, 20): + d = jan1 + timedelta(days=x) + h = is_holiday(d) + res[d.isoformat()] = 'holiday' if h else 'workday' + return res diff --git a/my/core/__main__.py b/my/core/__main__.py index 8d6fc2e..8b5d672 100644 --- a/my/core/__main__.py +++ b/my/core/__main__.py @@ -156,6 +156,8 @@ def modules_check(args): module: Optional[str] = args.module vw = '' if verbose else '; pass --verbose to print more information' + # todo force verbose if it's single module? + from . import common common.QUICK_STATS = quick # dirty, but hopefully OK for cli diff --git a/tests/calendar.py b/tests/calendar.py new file mode 100644 index 0000000..5264313 --- /dev/null +++ b/tests/calendar.py @@ -0,0 +1,6 @@ +from my.calendar.holidays import is_holiday + + +def test() -> None: + assert is_holiday('20190101') + assert not is_holiday('20180601') diff --git a/tox.ini b/tox.ini index b9e8637..542187c 100644 --- a/tox.ini +++ b/tox.ini @@ -20,6 +20,9 @@ commands = # my.time.tz.via_location dep pip install timezonefinder + # my.calendar.holidays dep + pip install workalendar + python3 -m pytest \ tests/core.py \ tests/misc.py \ @@ -29,7 +32,8 @@ commands = tests/demo.py \ tests/bluemaestro.py \ tests/location.py \ - tests/tz.py + tests/tz.py \ + tests/calendar.py # TODO add; once I figure out porg depdencency?? tests/config.py # TODO run demo.py? just make sure with_my is a bit cleverer? # TODO e.g. under CI, rely on installing @@ -68,6 +72,7 @@ commands = -p my.bluemaestro \ -p my.location.google \ -p my.time.tz.via_location \ + -p my.calendar.holidays \ --txt-report .mypy-coverage \ --html-report .mypy-coverage \ {posargs}