get sleep for each night

This commit is contained in:
Dima Gerasimov 2019-03-29 22:16:12 +00:00
parent 5658327883
commit e11cef6f91
2 changed files with 48 additions and 14 deletions

View file

@ -2,12 +2,17 @@ from kython import json_load
from datetime import datetime from datetime import datetime
from os.path import join from os.path import join
from functools import lru_cache from functools import lru_cache
import logging
from datetime import timedelta, datetime from datetime import timedelta, datetime, date
from typing import List, Dict, Iterator, NamedTuple from typing import List, Dict, Iterator, NamedTuple
from collections import OrderedDict as odict
fromts = datetime.fromtimestamp fromts = datetime.fromtimestamp
def get_logger():
return logging.getLogger('emfit-provider')
def hhmm(minutes): def hhmm(minutes):
return '{:02d}:{:02d}'.format(*divmod(minutes, 60)) return '{:02d}:{:02d}'.format(*divmod(minutes, 60))
@ -16,14 +21,12 @@ PATH = "/L/backups/emfit/"
EXCLUDED = [ EXCLUDED = [
'***REMOVED***', # pretty weird, detected sleep and HR (!) during the day when I was at work '***REMOVED***', # pretty weird, detected sleep and HR (!) during the day when I was at work
'***REMOVED***', '***REMOVED***',
'***REMOVED***', # some weird sleep during the day?
] ]
AWAKE = 4 AWAKE = 4
class Point(NamedTuple):
ts: int
pulse: float
class Emfit: class Emfit:
def __init__(self, sid: str, jj): def __init__(self, sid: str, jj):
self.sid = sid self.sid = sid
@ -37,6 +40,7 @@ class Emfit:
def hrv_evening(self): def hrv_evening(self):
return self.jj['hrv_rmssd_evening'] return self.jj['hrv_rmssd_evening']
# ok, I guess that's reasonable way of defining sleep date
@property @property
def date(self): def date(self):
return self.end.date() return self.end.date()
@ -59,6 +63,15 @@ class Emfit:
def epochs(self): def epochs(self):
return self.jj['sleep_epoch_datapoints'] return self.jj['sleep_epoch_datapoints']
@property
def epoch_series(self):
tss = []
eps = []
for [ts, e] in self.epochs:
tss.append(ts)
eps.append(e)
return tss, eps
@property # type: ignore @property # type: ignore
@lru_cache() @lru_cache()
def sleep_start(self) -> datetime: def sleep_start(self) -> datetime:
@ -98,6 +111,10 @@ class Emfit:
def hrv_hf(self): def hrv_hf(self):
return self.jj['hrv_hf'] return self.jj['hrv_hf']
@property
def recovery(self):
return self.hrv_morning - self.hrv_evening
@property @property
def summary(self): def summary(self):
return f"""in bed for {hhmm(self.time_in_bed)} return f"""in bed for {hhmm(self.time_in_bed)}
@ -105,7 +122,7 @@ emfit time: {hhmm(self.sleep_minutes_emfit)}; covered: {self.sleep_hr_coverage:.
hrv morning: {self.hrv_morning:.0f} hrv morning: {self.hrv_morning:.0f}
hrv evening: {self.hrv_evening:.0f} hrv evening: {self.hrv_evening:.0f}
avg hr: {self.measured_hr_avg:.0f} avg hr: {self.measured_hr_avg:.0f}
recovery: {self.hrv_morning - self.hrv_evening:3.0f} recovery: {self.recovery:3.0f}
{self.hrv_lf}/{self.hrv_hf}""" {self.hrv_lf}/{self.hrv_hf}"""
@property @property
@ -178,18 +195,22 @@ recovery: {self.hrv_morning - self.hrv_evening:3.0f}
# act in csv.. so it must be activity? wonder how is it measured. # act in csv.. so it must be activity? wonder how is it measured.
# but I guess makes sense. yeah, "measured_activity_avg": 595, about that # but I guess makes sense. yeah, "measured_activity_avg": 595, about that
# makes even more sense given tossturn datapoints only have timestamp # makes even more sense given tossturn datapoints only have timestamp
yield Point(ts=ts, pulse=pulse) yield ts, pulse
@property @property
def sleep_hr(self): def sleep_hr(self):
tss = [] tss = []
res = [] res = []
for p in self.iter_points(): for ts, pulse in self.iter_points():
if self.sleep_start < fromts(p.ts) < self.sleep_end: if self.sleep_start < fromts(ts) < self.sleep_end:
tss.append(p.ts) tss.append(ts)
res.append(p.pulse) res.append(pulse)
return tss, res return tss, res
@property
def sleep_hr_series(self):
return self.sleep_hr
@property @property
def hrv(self): def hrv(self):
tss = [] tss = []
@ -234,6 +255,18 @@ def iter_datas() -> Iterator[Emfit]:
def get_datas() -> List[Emfit]: def get_datas() -> List[Emfit]:
return list(sorted(list(iter_datas()), key=lambda e: e.start)) return list(sorted(list(iter_datas()), key=lambda e: e.start))
# TODO move away old entries if there is a diff?? # TODO move away old entries if there is a diff??
from kython import group_by_key
def by_night() -> Dict[date, Emfit]:
logger = get_logger()
res: Dict[date, Emfit] = odict()
# 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():
if len(sleeps) > 1:
logger.warning("multiple sleeps per night, not handled yet: %s", sleeps)
continue
[s] = sleeps
res[s.date] = s
return res

View file

@ -91,6 +91,7 @@ def plot_recovery_vs_hr_percentage():
# TODO ah. it's only last segment? # TODO ah. it's only last segment?
# ok, handled in dashboard now
def plot_hr(): def plot_hr():
jj = get_datas()[-1] jj = get_datas()[-1]
tss, uu = jj.sleep_hr tss, uu = jj.sleep_hr