prettify emfit and use common module

This commit is contained in:
Dima Gerasimov 2020-03-06 20:27:50 +00:00
parent 2daa9999fd
commit 55fd26acfc
2 changed files with 46 additions and 51 deletions

View file

@ -1,4 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
[[https://shop-eu.emfit.com/products/emfit-qs][Emfit QS]] sleep tracker
Consumes data exported by https://github.com/karlicoss/backup-emfit
"""
import json import json
import logging import logging
from collections import OrderedDict as odict from collections import OrderedDict as odict
@ -7,37 +12,33 @@ from datetime import date, datetime, time, timedelta
from pathlib import Path from pathlib import Path
from typing import Dict, Iterator, List, NamedTuple, Any, cast from typing import Dict, Iterator, List, NamedTuple, Any, cast
import kython
import pytz import pytz
from kython import cproperty, group_by_key
from cachew import cachew from ..common import get_files, LazyLogger, cproperty, group_by_key, mcachew
import mycfg
def get_logger(): logger = LazyLogger('my.emfit', level='info')
return logging.getLogger('emfit-provider')
# TODO FIXME remove?
import kython
timed = lambda f: kython.timed(f, logger=logger)
timed = lambda f: kython.timed(f, logger=get_logger())
def hhmm(minutes): def hhmm(minutes):
return '{:02d}:{:02d}'.format(*divmod(minutes, 60)) return '{:02d}:{:02d}'.format(*divmod(minutes, 60))
PATH = Path("/L/backups/emfit")
EXCLUDED = [
'***REMOVED***', # pretty weird, detected sleep and HR (!) during the day when I was at work
'***REMOVED***',
'***REMOVED***', # some weird sleep during the day?
]
AWAKE = 4 AWAKE = 4
Sid = str Sid = str
# TODO FIXME use tz provider for that? although emfit is always in london... # TODO use tz provider for that?
_TZ = pytz.timezone('Europe/London') _TZ = pytz.timezone(mycfg.emfit.tz)
# TODO use common tz thing?
def fromts(ts) -> datetime: def fromts(ts) -> datetime:
dt = datetime.fromtimestamp(ts) dt = datetime.fromtimestamp(ts)
return _TZ.localize(dt) return _TZ.localize(dt)
@ -281,8 +282,7 @@ class Emfit(Mixin):
@classmethod @classmethod
def make(cls, em) -> Iterator['Emfit']: def make(cls, em) -> Iterator['Emfit']:
# TODO FIXME res? # TODO FIXME Result type?
logger = get_logger()
if em.epochs is None: if em.epochs is None:
logger.error('%s (on %s) got None in epochs! ignoring', em.sid, em.date) logger.error('%s (on %s) got None in epochs! ignoring', em.sid, em.date)
return return
@ -299,18 +299,19 @@ def dir_hash(path: Path):
return mtimes return mtimes
@cachew(cache_path=Path('/L/data/.cache/emfit.cache'), hashf=dir_hash) @mcachew(cache_path=mycfg.emfit.cache_path, hashf=dir_hash, logger=logger)
def iter_datas_cached(path: Path) -> Iterator[Emfit]: def iter_datas_cached(path: Path) -> Iterator[Emfit]:
# TODO use get_files?
for f in sorted(path.glob('*.json')): for f in sorted(path.glob('*.json')):
sid = f.stem sid = f.stem
if sid in EXCLUDED: if sid in mycfg.emfit.excluded_sids:
continue continue
em = EmfitOld(sid=sid, jj=json.loads(f.read_text())) em = EmfitOld(sid=sid, jj=json.loads(f.read_text()))
yield from Emfit.make(em) yield from Emfit.make(em)
def iter_datas(path=PATH) -> Iterator[Emfit]: def iter_datas(path=mycfg.emfit.export_path) -> Iterator[Emfit]:
yield from iter_datas_cached(path) yield from iter_datas_cached(path)
@ -321,7 +322,6 @@ def get_datas() -> List[Emfit]:
@timed @timed
def by_night() -> Dict[date, Emfit]: def by_night() -> Dict[date, Emfit]:
logger = get_logger()
res: Dict[date, Emfit] = odict() res: Dict[date, Emfit] = odict()
# TODO shit. I need some sort of interrupted sleep detection? # TODO shit. I need some sort of interrupted sleep detection?
for dd, sleeps in group_by_key(get_datas(), key=lambda s: s.date).items(): for dd, sleeps in group_by_key(get_datas(), key=lambda s: s.date).items():
@ -333,38 +333,10 @@ def by_night() -> Dict[date, Emfit]:
return res return res
def test():
datas = get_datas()
for d in datas:
pass
def test_tz():
datas = get_datas()
for d in datas:
assert d.start.tzinfo is not None
assert d.end.tzinfo is not None
assert d.sleep_start.tzinfo is not None
assert d.sleep_end.tzinfo is not None
# https://qs.emfit.com/#/device/presence/***REMOVED***
# this was winter time, so GMT, UTC+0
sid_20190109 = '***REMOVED***'
[s0109] = [s for s in datas if s.sid == sid_20190109]
assert s0109.end.time() == time(hour=6, minute=42)
# summer time, so UTC+1
sid_20190411 = '***REMOVED***'
[s0411] = [s for s in datas if s.sid == sid_20190411]
assert s0411.end.time() == time(hour=9, minute=30)
def main(): def main():
from kython.klogging import setup_logzero
setup_logzero(get_logger(), level=logging.DEBUG)
for k, v in by_night().items(): for k, v in by_night().items():
print(k, v.start, v.end) print(k, v.start, v.end)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

23
tests/emfit.py Normal file
View file

@ -0,0 +1,23 @@
from my.emfit import get_datas
def test():
datas = get_datas()
for d in datas:
assert d.start.tzinfo is not None
assert d.end.tzinfo is not None
assert d.sleep_start.tzinfo is not None
assert d.sleep_end.tzinfo is not None
def test_tz():
datas = get_datas()
# this was winter time, so GMT, UTC+0
[s0109] = [s for s in datas if s.date.strftime('%Y%m%d') == '20190109']
assert s0109.end.strftime('%H:%M') == '06:42'
# TODO FIXME ugh, it's broken?...
# summer time, so UTC+1
[s0411] = [s for s in datas if s.date.strftime('%Y%m%d') == '20190411']
assert s0411.end.strftime('%H:%M') == '09:30'