99 lines
2.2 KiB
Python
99 lines
2.2 KiB
Python
'''
|
|
Weight data (manually logged)
|
|
'''
|
|
|
|
from collections.abc import Iterator
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from my import orgmode
|
|
from my.core import make_logger
|
|
from my.core.error import Res, extract_error_datetime, set_error_datetime
|
|
|
|
config = Any
|
|
|
|
|
|
def make_config() -> config:
|
|
from my.config import weight as user_config # type: ignore[attr-defined]
|
|
|
|
return user_config()
|
|
|
|
|
|
log = make_logger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class Entry:
|
|
dt: datetime
|
|
value: float
|
|
# TODO comment??
|
|
|
|
|
|
Result = Res[Entry]
|
|
|
|
|
|
def from_orgmode() -> Iterator[Result]:
|
|
cfg = make_config()
|
|
|
|
orgs = orgmode.query()
|
|
for o in orgmode.query().all():
|
|
if 'weight' not in o.tags:
|
|
continue
|
|
try:
|
|
# TODO can it throw? not sure
|
|
created = o.created
|
|
assert created is not None
|
|
except Exception as e:
|
|
log.exception(e)
|
|
yield e
|
|
continue
|
|
try:
|
|
w = float(o.heading)
|
|
except Exception as e:
|
|
set_error_datetime(e, dt=created)
|
|
log.exception(e)
|
|
yield e
|
|
continue
|
|
# FIXME use timezone provider
|
|
created = cfg.default_timezone.localize(created)
|
|
assert created is not None # ??? somehow mypy wasn't happy?
|
|
yield Entry(
|
|
dt=created,
|
|
value=w,
|
|
# TODO add org note content as comment?
|
|
)
|
|
|
|
|
|
def make_dataframe(data: Iterator[Result]):
|
|
import pandas as pd
|
|
|
|
def it():
|
|
for e in data:
|
|
if isinstance(e, Exception):
|
|
dt = extract_error_datetime(e)
|
|
yield {
|
|
'dt': dt,
|
|
'error': str(e),
|
|
}
|
|
else:
|
|
yield {
|
|
'dt': e.dt,
|
|
'weight': e.value,
|
|
}
|
|
|
|
df = pd.DataFrame(it())
|
|
df = df.set_index('dt')
|
|
# TODO not sure about UTC??
|
|
df.index = pd.to_datetime(df.index, utc=True)
|
|
return df
|
|
|
|
|
|
def dataframe():
|
|
entries = from_orgmode()
|
|
return make_dataframe(entries)
|
|
|
|
|
|
# TODO move to a submodule? e.g. my.body.weight.orgmode?
|
|
# so there could be more sources
|
|
# not sure about my.body thing though
|