tests: move remaining tests from tests/ to my.tests, cleanup corresponding modules
This commit is contained in:
parent
a5643206a0
commit
b87d1c970a
9 changed files with 120 additions and 134 deletions
|
@ -2,21 +2,29 @@
|
||||||
Weight data (manually logged)
|
Weight data (manually logged)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import NamedTuple, Iterator
|
from typing import Any, Iterator
|
||||||
|
|
||||||
from ..core import LazyLogger
|
from my.core import make_logger
|
||||||
from ..core.error import Res, set_error_datetime, extract_error_datetime
|
from my.core.error import Res, extract_error_datetime, set_error_datetime
|
||||||
|
|
||||||
from .. import orgmode
|
from my import orgmode
|
||||||
|
|
||||||
from my.config import weight as config # type: ignore[attr-defined]
|
config = Any
|
||||||
|
|
||||||
|
|
||||||
log = LazyLogger('my.body.weight')
|
def make_config() -> config:
|
||||||
|
from my.config import weight as user_config # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
return user_config()
|
||||||
|
|
||||||
|
|
||||||
class Entry(NamedTuple):
|
log = make_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Entry:
|
||||||
dt: datetime
|
dt: datetime
|
||||||
value: float
|
value: float
|
||||||
# TODO comment??
|
# TODO comment??
|
||||||
|
@ -26,6 +34,8 @@ Result = Res[Entry]
|
||||||
|
|
||||||
|
|
||||||
def from_orgmode() -> Iterator[Result]:
|
def from_orgmode() -> Iterator[Result]:
|
||||||
|
cfg = make_config()
|
||||||
|
|
||||||
orgs = orgmode.query()
|
orgs = orgmode.query()
|
||||||
for o in orgmode.query().all():
|
for o in orgmode.query().all():
|
||||||
if 'weight' not in o.tags:
|
if 'weight' not in o.tags:
|
||||||
|
@ -46,8 +56,8 @@ def from_orgmode() -> Iterator[Result]:
|
||||||
yield e
|
yield e
|
||||||
continue
|
continue
|
||||||
# FIXME use timezone provider
|
# FIXME use timezone provider
|
||||||
created = config.default_timezone.localize(created)
|
created = cfg.default_timezone.localize(created)
|
||||||
assert created is not None #??? somehow mypy wasn't happy?
|
assert created is not None # ??? somehow mypy wasn't happy?
|
||||||
yield Entry(
|
yield Entry(
|
||||||
dt=created,
|
dt=created,
|
||||||
value=w,
|
value=w,
|
||||||
|
@ -57,19 +67,21 @@ def from_orgmode() -> Iterator[Result]:
|
||||||
|
|
||||||
def make_dataframe(data: Iterator[Result]):
|
def make_dataframe(data: Iterator[Result]):
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
def it():
|
def it():
|
||||||
for e in data:
|
for e in data:
|
||||||
if isinstance(e, Exception):
|
if isinstance(e, Exception):
|
||||||
dt = extract_error_datetime(e)
|
dt = extract_error_datetime(e)
|
||||||
yield {
|
yield {
|
||||||
'dt' : dt,
|
'dt': dt,
|
||||||
'error': str(e),
|
'error': str(e),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
yield {
|
yield {
|
||||||
'dt' : e.dt,
|
'dt': e.dt,
|
||||||
'weight': e.value,
|
'weight': e.value,
|
||||||
}
|
}
|
||||||
|
|
||||||
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??
|
# TODO not sure about UTC??
|
||||||
|
@ -81,6 +93,7 @@ def dataframe():
|
||||||
entries = from_orgmode()
|
entries = from_orgmode()
|
||||||
return make_dataframe(entries)
|
return make_dataframe(entries)
|
||||||
|
|
||||||
|
|
||||||
# TODO move to a submodule? e.g. my.body.weight.orgmode?
|
# TODO move to a submodule? e.g. my.body.weight.orgmode?
|
||||||
# so there could be more sources
|
# so there could be more sources
|
||||||
# not sure about my.body thing though
|
# not sure about my.body thing though
|
||||||
|
|
|
@ -6,18 +6,28 @@ REQUIRES = [
|
||||||
'orgparse',
|
'orgparse',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
from typing import Iterable, List, NamedTuple, Optional, Sequence, Tuple
|
||||||
from typing import List, Sequence, Iterable, NamedTuple, Optional, Tuple
|
|
||||||
|
|
||||||
from my.core import get_files
|
import orgparse
|
||||||
|
|
||||||
|
from my.core import Paths, Stats, get_files, stat
|
||||||
from my.core.cachew import cache_dir, mcachew
|
from my.core.cachew import cache_dir, mcachew
|
||||||
from my.core.orgmode import collect
|
from my.core.orgmode import collect
|
||||||
|
|
||||||
from my.config import orgmode as user_config
|
|
||||||
|
|
||||||
import orgparse
|
class config:
|
||||||
|
paths: Paths
|
||||||
|
|
||||||
|
|
||||||
|
def make_config() -> config:
|
||||||
|
from my.config import orgmode as user_config
|
||||||
|
|
||||||
|
class combined_config(user_config, config): ...
|
||||||
|
|
||||||
|
return combined_config()
|
||||||
|
|
||||||
|
|
||||||
# temporary? hack to cache org-mode notes
|
# temporary? hack to cache org-mode notes
|
||||||
|
@ -28,10 +38,13 @@ class OrgNote(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
def inputs() -> Sequence[Path]:
|
def inputs() -> Sequence[Path]:
|
||||||
return get_files(user_config.paths)
|
cfg = make_config()
|
||||||
|
return get_files(cfg.paths)
|
||||||
|
|
||||||
|
|
||||||
_rgx = re.compile(orgparse.date.gene_timestamp_regex(brtype='inactive'), re.VERBOSE)
|
_rgx = re.compile(orgparse.date.gene_timestamp_regex(brtype='inactive'), re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
def _created(n: orgparse.OrgNode) -> Tuple[Optional[datetime], str]:
|
def _created(n: orgparse.OrgNode) -> Tuple[Optional[datetime], str]:
|
||||||
heading = n.heading
|
heading = n.heading
|
||||||
# meh.. support in orgparse?
|
# meh.. support in orgparse?
|
||||||
|
@ -41,7 +54,7 @@ def _created(n: orgparse.OrgNode) -> Tuple[Optional[datetime], str]:
|
||||||
# try to guess from heading
|
# try to guess from heading
|
||||||
m = _rgx.search(heading)
|
m = _rgx.search(heading)
|
||||||
if m is not None:
|
if m is not None:
|
||||||
createds = m.group(0) # could be None
|
createds = m.group(0) # could be None
|
||||||
if createds is None:
|
if createds is None:
|
||||||
return (None, heading)
|
return (None, heading)
|
||||||
assert isinstance(createds, str)
|
assert isinstance(createds, str)
|
||||||
|
@ -67,7 +80,7 @@ def to_note(x: orgparse.OrgNode) -> OrgNote:
|
||||||
created = None
|
created = None
|
||||||
return OrgNote(
|
return OrgNote(
|
||||||
created=created,
|
created=created,
|
||||||
heading=heading, # todo include the body?
|
heading=heading, # todo include the body?
|
||||||
tags=list(x.tags),
|
tags=list(x.tags),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,7 +104,8 @@ class Query:
|
||||||
|
|
||||||
# TODO yield errors?
|
# TODO yield errors?
|
||||||
@mcachew(
|
@mcachew(
|
||||||
cache_path=_cachew_cache_path, force_file=True,
|
cache_path=_cachew_cache_path,
|
||||||
|
force_file=True,
|
||||||
depends_on=_cachew_depends_on,
|
depends_on=_cachew_depends_on,
|
||||||
)
|
)
|
||||||
def _iterate(self, f: Path) -> Iterable[OrgNote]:
|
def _iterate(self, f: Path) -> Iterable[OrgNote]:
|
||||||
|
@ -114,8 +128,8 @@ def query() -> Query:
|
||||||
return Query(files=inputs())
|
return Query(files=inputs())
|
||||||
|
|
||||||
|
|
||||||
from my.core import Stats, stat
|
|
||||||
def stats() -> Stats:
|
def stats() -> Stats:
|
||||||
def outlines():
|
def outlines():
|
||||||
return query().all()
|
return query().all()
|
||||||
|
|
||||||
return stat(outlines)
|
return stat(outlines)
|
||||||
|
|
|
@ -10,7 +10,7 @@ REQUIRES = [
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterator, List, NamedTuple, Optional, Protocol, Sequence
|
from typing import Iterator, List, NamedTuple, Optional, Protocol, Sequence, TYPE_CHECKING
|
||||||
|
|
||||||
import pdfannots
|
import pdfannots
|
||||||
from more_itertools import bucket
|
from more_itertools import bucket
|
||||||
|
@ -185,8 +185,6 @@ def stats() -> Stats:
|
||||||
|
|
||||||
|
|
||||||
### legacy/misc stuff
|
### legacy/misc stuff
|
||||||
iter_annotations = annotations # for backwards compatibility
|
if not TYPE_CHECKING:
|
||||||
|
iter_annotations = annotations
|
||||||
###
|
###
|
||||||
|
|
||||||
# can use 'hpi query my.pdfs.annotations -o pprint' to test
|
|
||||||
#
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from pathlib import Path
|
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
|
import pytest
|
||||||
from more_itertools import one
|
from more_itertools import one
|
||||||
|
|
||||||
import pytest
|
from my.bluemaestro import Measurement, measurements
|
||||||
|
from my.core.cfg import tmp_config
|
||||||
|
|
||||||
|
from .common import testdata
|
||||||
from my.bluemaestro import measurements, Measurement
|
|
||||||
|
|
||||||
|
|
||||||
def ok_measurements() -> Iterator[Measurement]:
|
def ok_measurements() -> Iterator[Measurement]:
|
||||||
|
@ -26,7 +26,7 @@ def test() -> None:
|
||||||
# check that timezone is set properly
|
# check that timezone is set properly
|
||||||
assert dts == '20200824 22'
|
assert dts == '20200824 22'
|
||||||
|
|
||||||
assert len(tp) == 1 # should be unique
|
assert len(tp) == 1 # should be unique
|
||||||
|
|
||||||
# 2.5 K + 4 K datapoints, somewhat overlapping
|
# 2.5 K + 4 K datapoints, somewhat overlapping
|
||||||
assert len(res2020) < 6000
|
assert len(res2020) < 6000
|
||||||
|
@ -46,14 +46,12 @@ def test_old_db() -> None:
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def prepare():
|
def prepare():
|
||||||
from my.tests.common import testdata
|
|
||||||
bmdata = testdata() / 'hpi-testdata' / 'bluemaestro'
|
bmdata = testdata() / 'hpi-testdata' / 'bluemaestro'
|
||||||
assert bmdata.exists(), bmdata
|
assert bmdata.exists(), bmdata
|
||||||
|
|
||||||
class bluemaestro:
|
class bluemaestro:
|
||||||
export_path = bmdata
|
export_path = bmdata
|
||||||
|
|
||||||
from my.core.cfg import tmp_config
|
|
||||||
with tmp_config() as config:
|
with tmp_config() as config:
|
||||||
config.bluemaestro = bluemaestro
|
config.bluemaestro = bluemaestro
|
||||||
yield
|
yield
|
57
my/tests/body/weight.py
Normal file
57
my/tests/body/weight.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
from pathlib import Path
|
||||||
|
import pytz
|
||||||
|
from my.core.cfg import tmp_config
|
||||||
|
import pytest
|
||||||
|
from my.body.weight import from_orgmode
|
||||||
|
|
||||||
|
|
||||||
|
def test_body_weight() -> None:
|
||||||
|
weights = [0.0 if isinstance(x, Exception) else x.value for x in from_orgmode()]
|
||||||
|
|
||||||
|
assert weights == [
|
||||||
|
0.0,
|
||||||
|
62.0,
|
||||||
|
0.0,
|
||||||
|
61.0,
|
||||||
|
62.0,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def prepare(tmp_path: Path):
|
||||||
|
ndir = tmp_path / 'notes'
|
||||||
|
ndir.mkdir()
|
||||||
|
logs = ndir / 'logs.org'
|
||||||
|
logs.write_text(
|
||||||
|
'''
|
||||||
|
#+TITLE: Stuff I'm logging
|
||||||
|
|
||||||
|
* Weight (org-capture) :weight:
|
||||||
|
** [2020-05-01 Fri 09:00] 62
|
||||||
|
** 63
|
||||||
|
this should be ignored, got no timestamp
|
||||||
|
** [2020-05-03 Sun 08:00] 61
|
||||||
|
** [2020-05-04 Mon 10:00] 62
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
misc = ndir / 'misc.org'
|
||||||
|
misc.write_text(
|
||||||
|
'''
|
||||||
|
Some misc stuff
|
||||||
|
|
||||||
|
* unrelated note :weight:whatever:
|
||||||
|
'''
|
||||||
|
)
|
||||||
|
|
||||||
|
class orgmode:
|
||||||
|
paths = [ndir]
|
||||||
|
|
||||||
|
class weight:
|
||||||
|
# TODO ugh. this belongs to tz provider or global config or something
|
||||||
|
default_timezone = pytz.timezone('Europe/London')
|
||||||
|
|
||||||
|
with tmp_config() as cfg:
|
||||||
|
cfg.orgmode = orgmode
|
||||||
|
cfg.weight = weight
|
||||||
|
yield
|
|
@ -5,9 +5,8 @@ import pytest
|
||||||
from more_itertools import ilen
|
from more_itertools import ilen
|
||||||
|
|
||||||
from my.core.cfg import tmp_config
|
from my.core.cfg import tmp_config
|
||||||
from my.tests.common import testdata
|
|
||||||
|
|
||||||
from my.pdfs import annotated_pdfs, annotations, get_annots
|
from my.pdfs import annotated_pdfs, annotations, get_annots
|
||||||
|
from my.tests.common import testdata
|
||||||
|
|
||||||
|
|
||||||
def test_module(with_config) -> None:
|
def test_module(with_config) -> None:
|
|
@ -1,15 +1,16 @@
|
||||||
|
import pytest
|
||||||
|
from more_itertools import consume
|
||||||
|
|
||||||
from my.core.cfg import tmp_config
|
from my.core.cfg import tmp_config
|
||||||
from my.core.utils.itertools import ensure_unique
|
from my.core.utils.itertools import ensure_unique
|
||||||
|
|
||||||
# todo ugh, it's discovered as a test???
|
|
||||||
from .common import testdata
|
from .common import testdata
|
||||||
|
|
||||||
from more_itertools import consume
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
# deliberately use mixed style imports on the top level and inside the methods to test tmp_config stuff
|
# deliberately use mixed style imports on the top level and inside the methods to test tmp_config stuff
|
||||||
import my.reddit.rexport as my_reddit_rexport
|
# todo won't really be necessary once we migrate to lazy user config
|
||||||
import my.reddit.all as my_reddit_all
|
import my.reddit.all as my_reddit_all
|
||||||
|
import my.reddit.rexport as my_reddit_rexport
|
||||||
|
|
||||||
|
|
||||||
def test_basic_1() -> None:
|
def test_basic_1() -> None:
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
# TODO move this somewhere else -- there are more specific tests covering this now
|
|
||||||
def test_dynamic_configuration(notes: Path) -> None:
|
|
||||||
import pytz
|
|
||||||
from types import SimpleNamespace as NS
|
|
||||||
|
|
||||||
from my.core.cfg import tmp_config
|
|
||||||
with tmp_config() as C:
|
|
||||||
C.orgmode = NS(paths=[notes])
|
|
||||||
# TODO ugh. this belongs to tz provider or global config or something
|
|
||||||
C.weight = NS(default_timezone=pytz.timezone('Europe/London'))
|
|
||||||
|
|
||||||
from my.body.weight import from_orgmode
|
|
||||||
weights = [0.0 if isinstance(x, Exception) else x.value for x in from_orgmode()]
|
|
||||||
|
|
||||||
assert weights == [
|
|
||||||
0.0,
|
|
||||||
62.0,
|
|
||||||
0.0,
|
|
||||||
61.0,
|
|
||||||
62.0,
|
|
||||||
0.0,
|
|
||||||
]
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
|
|
||||||
# TODO this test should probs be deprecated? it's more of a documentation?
|
|
||||||
def test_user_config() -> None:
|
|
||||||
from my.core.common import classproperty
|
|
||||||
class user_config:
|
|
||||||
param1 = 'abacaba'
|
|
||||||
# TODO fuck. properties don't work here???
|
|
||||||
@classproperty
|
|
||||||
def param2(cls) -> int:
|
|
||||||
return 456
|
|
||||||
|
|
||||||
extra = 'extra!'
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class test_config(user_config):
|
|
||||||
param1: str
|
|
||||||
param2: int # type: ignore[assignment] # TODO need to figure out how to trick mypy for @classproperty
|
|
||||||
param3: str = 'default'
|
|
||||||
|
|
||||||
assert test_config.param1 == 'abacaba'
|
|
||||||
assert test_config.param2 == 456
|
|
||||||
assert test_config.param3 == 'default'
|
|
||||||
assert test_config.extra == 'extra!'
|
|
||||||
|
|
||||||
from my.core.cfg import make_config
|
|
||||||
c = make_config(test_config)
|
|
||||||
assert c.param1 == 'abacaba'
|
|
||||||
assert c.param2 == 456
|
|
||||||
assert c.param3 == 'default'
|
|
||||||
assert c.extra == 'extra!'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def notes(tmp_path: Path):
|
|
||||||
ndir = tmp_path / 'notes'
|
|
||||||
ndir.mkdir()
|
|
||||||
logs = ndir / 'logs.org'
|
|
||||||
logs.write_text('''
|
|
||||||
#+TITLE: Stuff I'm logging
|
|
||||||
|
|
||||||
* Weight (org-capture) :weight:
|
|
||||||
** [2020-05-01 Fri 09:00] 62
|
|
||||||
** 63
|
|
||||||
this should be ignored, got no timestamp
|
|
||||||
** [2020-05-03 Sun 08:00] 61
|
|
||||||
** [2020-05-04 Mon 10:00] 62
|
|
||||||
''')
|
|
||||||
misc = ndir / 'misc.org'
|
|
||||||
misc.write_text('''
|
|
||||||
Some misc stuff
|
|
||||||
|
|
||||||
* unrelated note :weight:whatever:
|
|
||||||
''')
|
|
||||||
try:
|
|
||||||
yield ndir
|
|
||||||
finally:
|
|
||||||
pass
|
|
5
tox.ini
5
tox.ini
|
@ -86,11 +86,6 @@ commands =
|
||||||
--pyargs {[testenv]package_name}.core {[testenv]package_name}.tests \
|
--pyargs {[testenv]package_name}.core {[testenv]package_name}.tests \
|
||||||
{posargs}
|
{posargs}
|
||||||
|
|
||||||
{envpython} -m pytest tests \
|
|
||||||
# ignore some tests which might take a while to run on ci..
|
|
||||||
--ignore tests/extra/polar.py
|
|
||||||
{posargs}
|
|
||||||
|
|
||||||
|
|
||||||
[testenv:demo]
|
[testenv:demo]
|
||||||
commands =
|
commands =
|
||||||
|
|
Loading…
Add table
Reference in a new issue