core: experiments with attaching datetime information to errors

my.body.weigth: put datetime information in the error rows
This commit is contained in:
Dima Gerasimov 2020-09-09 21:28:22 +01:00 committed by karlicoss
parent 743312a87b
commit 99e50f0afe
5 changed files with 60 additions and 11 deletions

View file

@ -5,8 +5,9 @@ Weight data (manually logged)
from datetime import datetime from datetime import datetime
from typing import NamedTuple, Iterator from typing import NamedTuple, Iterator
from ..common import LazyLogger from ..core import LazyLogger
from ..error import Res from ..core.error import Res, set_error_datetime, extract_error_datetime
from ..notes import orgmode from ..notes import orgmode
from my.config import weight as config from my.config import weight as config
@ -24,7 +25,7 @@ class Entry(NamedTuple):
Result = Res[Entry] Result = Res[Entry]
# TODO cachew? # TODO cachew? but in order for that to work, would need timestamps for input org-mode files..
def from_orgmode() -> Iterator[Result]: def from_orgmode() -> Iterator[Result]:
orgs = orgmode.query() orgs = orgmode.query()
for o in orgs.query_all(lambda o: o.with_tag('weight')): for o in orgs.query_all(lambda o: o.with_tag('weight')):
@ -39,10 +40,11 @@ def from_orgmode() -> Iterator[Result]:
try: try:
w = float(o.heading) w = float(o.heading)
except Exception as e: except Exception as e:
set_error_datetime(e, dt=created)
log.exception(e) log.exception(e)
yield e yield e
continue continue
# TODO not sure if it's really necessary.. # todo perhaps, better to use timezone provider
created = config.default_timezone.localize(created) created = config.default_timezone.localize(created)
yield Entry( yield Entry(
dt=created, dt=created,
@ -57,7 +59,9 @@ def dataframe():
def it(): def it():
for e in from_orgmode(): for e in from_orgmode():
if isinstance(e, Exception): if isinstance(e, Exception):
dt = extract_error_datetime(e)
yield { yield {
'dt' : dt,
'error': str(e), 'error': str(e),
} }
else: else:
@ -67,6 +71,7 @@ def dataframe():
} }
df = pd.DataFrame(it()) df = pd.DataFrame(it())
df.set_index('dt', inplace=True) df.set_index('dt', inplace=True)
# TODO not sure about UTC??
df.index = pd.to_datetime(df.index, utc=True) df.index = pd.to_datetime(df.index, utc=True)
return df return df

View file

@ -4,7 +4,7 @@ See https://beepb00p.xyz/mypy-error-handling.html#kiss for more detail
""" """
from itertools import tee from itertools import tee
from typing import Union, TypeVar, Iterable, List, Tuple, Type from typing import Union, TypeVar, Iterable, List, Tuple, Type, Optional
T = TypeVar('T') T = TypeVar('T')
@ -97,3 +97,47 @@ def test_sort_res_by() -> None:
results2 = sort_res_by(ress + [0], lambda x: x) # type: ignore results2 = sort_res_by(ress + [0], lambda x: x) # type: ignore
assert results2 == [Exc('last'), 0] + results[:-1] assert results2 == [Exc('last'), 0] + results[:-1]
# helpers to associate timestamps with the errors (so something meaningful could be displayed on the plots, for example)
# todo document it under 'patterns' somewhere...
# todo proper typevar?
from datetime import datetime
def set_error_datetime(e: Exception, dt: datetime) -> None:
# at the moment, we're using isoformat() instead of datetime directly to make it cachew-friendly
# once cachew preserves exception argument types, we can remove these hacks
e.args = e.args + (dt.isoformat(), )
# todo not sure if should return new exception?
def extract_error_datetime(e: Exception) -> Optional[datetime]:
from .common import fromisoformat
import re
# TODO FIXME meh. definitely need to preserve exception args types in cachew if possible..
for x in reversed(e.args):
m = re.search(r'\d{4}.*T.*:..(\.\d{6})?(\+.....)?', x)
if m is None:
continue
ss = m.group(0)
# todo not sure if should be defensive??
return fromisoformat(ss)
return None
def test_datetime_errors():
import pytz
dt_notz = datetime.now()
dt_tz = datetime.now(tz=pytz.timezone('Europe/Amsterdam'))
for dt in [dt_tz, dt_notz]:
e1 = RuntimeError('whatever')
assert extract_error_datetime(e1) is None
set_error_datetime(e1, dt=dt)
assert extract_error_datetime(e1) == dt
# test that cachew can handle it...
e2 = RuntimeError(str(e1.args))
assert extract_error_datetime(e2) == dt
e3 = RuntimeError(str(['one', '2019-11-27T08:56:00', 'three']))
assert extract_error_datetime(e3) is not None

View file

@ -69,11 +69,10 @@ def pages() -> List[Res[Page]]:
# todo not public api yet # todo not public api yet
def stats(): def stats():
# todo add 'last date' checks et from .core import stat
return { return {
# todo ilen **stat(highlights),
'highlights': len(highlights()), **stat(pages),
'pages' : len(pages()),
} }

View file

@ -2,6 +2,7 @@
# for now, running with with_my pytest tests/reddit.py # for now, running with with_my pytest tests/reddit.py
# discover __init__,py as well # discover __init__,py as well
# TODO also importing too much with it... # TODO also importing too much with it...
# TODO FIXME wtf is this??
python_files = reddit.py python_files = reddit.py
addopts = addopts =
--verbose --verbose

View file

@ -46,8 +46,8 @@ def prepare(tmp_path: Path):
pass pass
# meh # meh. otherwise was struggling to run directly against my.core.error...
from my.core.error import test_sort_res_by from my.core.error import *
from typing import Iterable, List from typing import Iterable, List