From 97035d8e4cae649505c97d17e4c21b072dbc9813 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 6 Dec 2021 22:37:23 +0100 Subject: [PATCH 1/6] Switch layout to src folder layout --- .gitignore | 2 +- habitmove/__init__.py | 4 - poetry.lock | 166 +++++++++++++++++++- pyproject.toml | 10 +- src/habitmove/__init__.py | 9 ++ run.py => src/habitmove/cli.py | 13 +- {habitmove => src/habitmove}/habits.py | 0 {habitmove => src/habitmove}/loopdata.py | 0 {habitmove => src/habitmove}/nomie.py | 9 +- {habitmove => src/habitmove}/nomiedata.py | 0 {habitmove => src/habitmove}/repetitions.py | 0 {habitmove => src/habitmove}/schema.py | 0 12 files changed, 196 insertions(+), 17 deletions(-) delete mode 100644 habitmove/__init__.py create mode 100644 src/habitmove/__init__.py rename run.py => src/habitmove/cli.py (69%) rename {habitmove => src/habitmove}/habits.py (100%) rename {habitmove => src/habitmove}/loopdata.py (100%) rename {habitmove => src/habitmove}/nomie.py (94%) rename {habitmove => src/habitmove}/nomiedata.py (100%) rename {habitmove => src/habitmove}/repetitions.py (100%) rename {habitmove => src/habitmove}/schema.py (100%) diff --git a/.gitignore b/.gitignore index 7397c55..6fe089d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -data/ +testdata/ output.db # Created by https://www.toptal.com/developers/gitignore/api/vim,linux,python,pandas diff --git a/habitmove/__init__.py b/habitmove/__init__.py deleted file mode 100644 index 074201a..0000000 --- a/habitmove/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -import habitmove.schema as schema -import habitmove.habits as habits -import habitmove.repetitions as rep -import habitmove.nomie as nomie diff --git a/poetry.lock b/poetry.lock index c93bb8f..714bcdc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,8 +1,170 @@ -package = [] +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "click" +version = "8.0.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyparsing" +version = "3.0.6" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "ce2aa767160f871dd3652615ba0a0dceb7733d62eb8cb4665b87f30a562e3adf" +content-hash = "11b15112d348ca956ae061f53d51b37fcb0adb9b06f9b1245c4a93eddc08bb1f" [metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +click = [ + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyparsing = [ + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, +] +pytest = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] diff --git a/pyproject.toml b/pyproject.toml index 15e602e..47a9483 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,18 +1,22 @@ [tool.poetry] name = "habitmove" -version = "0.4" +version = "0.4.0" description = "migrate nomie data to loop habits tracker" authors = ["Marty Oehme "] +packages = [ + { include = "habitmove", from = "src"}, +] [tool.poetry.dependencies] +importlib-metadata = {version = "^1.0", python = "<3.8"} python = "^3.9" +click = "^8.0" [tool.poetry.dev-dependencies] [tool.poetry.scripts] -habitmove = "run:main" +habitmove = "habitmove.cli:main" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" - diff --git a/src/habitmove/__init__.py b/src/habitmove/__init__.py new file mode 100644 index 0000000..4ed1b57 --- /dev/null +++ b/src/habitmove/__init__.py @@ -0,0 +1,9 @@ +# init.py +import sys + +if sys.version_info >= (3, 8): + from importlib.metadata import version as metadata_version +else: + from importlib_metadata import version as metadata_version + +__version__ = str(metadata_version(__name__)) diff --git a/run.py b/src/habitmove/cli.py similarity index 69% rename from run.py rename to src/habitmove/cli.py index 4456f1a..acc2a66 100755 --- a/run.py +++ b/src/habitmove/cli.py @@ -5,6 +5,9 @@ import habitmove.repetitions as rep import habitmove.nomie as nomie from habitmove.nomiedata import NomieImport +import click +from . import __version__ + import sys @@ -25,9 +28,13 @@ def migrate(data: NomieImport): db.close() -def main(): - file = sys.argv[1] - data = nomie.get_data(file) +@click.command() +@click.version_option(version=__version__) +@click.argument("inputfile") +def main(inputfile): + # TODO test and error gracefully for no input given + # file = sys.argv[1] + data = nomie.get_data(inputfile) migrate(data) diff --git a/habitmove/habits.py b/src/habitmove/habits.py similarity index 100% rename from habitmove/habits.py rename to src/habitmove/habits.py diff --git a/habitmove/loopdata.py b/src/habitmove/loopdata.py similarity index 100% rename from habitmove/loopdata.py rename to src/habitmove/loopdata.py diff --git a/habitmove/nomie.py b/src/habitmove/nomie.py similarity index 94% rename from habitmove/nomie.py rename to src/habitmove/nomie.py index 4764272..847edf6 100644 --- a/habitmove/nomie.py +++ b/src/habitmove/nomie.py @@ -2,6 +2,7 @@ import json import re +from click import secho, echo from habitmove.nomiedata import Tracker, Event, Activity, NomieImport @@ -35,13 +36,13 @@ def verify_continue(data: NomieImport): for e in data.events: activity_count += len(e.activities) if e.activities else 0 - print(f"Exporting from nomie {data.version}:") - print(f"Found trackers: {trackers}") - print( + secho(f"Exporting from nomie {data.version}:", fg="green") + echo(f"Found trackers: {trackers}") + echo( f"Found events: {len(data.events)} entries, containing {activity_count} individual activities." ) if not confirmation_question("Do you want to continue?", default_no=False): - print("Aborted.") + echo("Aborted.") exit(0) diff --git a/habitmove/nomiedata.py b/src/habitmove/nomiedata.py similarity index 100% rename from habitmove/nomiedata.py rename to src/habitmove/nomiedata.py diff --git a/habitmove/repetitions.py b/src/habitmove/repetitions.py similarity index 100% rename from habitmove/repetitions.py rename to src/habitmove/repetitions.py diff --git a/habitmove/schema.py b/src/habitmove/schema.py similarity index 100% rename from habitmove/schema.py rename to src/habitmove/schema.py From 2d2b4430ffd6844c99b7773bb2dc81c3bc8cc1c8 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 6 Dec 2021 23:20:18 +0100 Subject: [PATCH 2/6] Add initial cli test --- .gitignore | 2 +- pyproject.toml | 1 + src/habitmove/cli.py | 5 ----- tests/__init__.py | 0 tests/test_cli.py | 10 ++++++++++ 5 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_cli.py diff --git a/.gitignore b/.gitignore index 6fe089d..7397c55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -testdata/ +data/ output.db # Created by https://www.toptal.com/developers/gitignore/api/vim,linux,python,pandas diff --git a/pyproject.toml b/pyproject.toml index 47a9483..a7b28dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ python = "^3.9" click = "^8.0" [tool.poetry.dev-dependencies] +pytest = "^6.2" [tool.poetry.scripts] habitmove = "habitmove.cli:main" diff --git a/src/habitmove/cli.py b/src/habitmove/cli.py index acc2a66..489cb8a 100755 --- a/src/habitmove/cli.py +++ b/src/habitmove/cli.py @@ -9,9 +9,6 @@ import click from . import __version__ -import sys - - def migrate(data: NomieImport): db = schema.migrate("output.db") if not db: @@ -32,8 +29,6 @@ def migrate(data: NomieImport): @click.version_option(version=__version__) @click.argument("inputfile") def main(inputfile): - # TODO test and error gracefully for no input given - # file = sys.argv[1] data = nomie.get_data(inputfile) migrate(data) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..ffd50e3 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,10 @@ +import click.testing + +from habitmove import cli + + +def test_cli_fails_without_file(): + runner = click.testing.CliRunner() + result = runner.invoke(cli.main) + assert result.exit_code == 2 + assert "Missing argument" in result.output From 09cbab902184427366366abf41ad05ccee49438f Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 6 Dec 2021 23:36:24 +0100 Subject: [PATCH 3/6] Add code coverage gathering --- poetry.lock | 96 ++++++++++++++++++++++++++++++++++++++- pyproject.toml | 13 ++++++ src/habitmove/loopdata.py | 1 - tests/test_cli.py | 9 +++- 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 714bcdc..015f3c1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -39,6 +39,20 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "coverage" +version = "6.2" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "iniconfig" version = "1.1.1" @@ -110,6 +124,21 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + [[package]] name = "toml" version = "0.10.2" @@ -118,10 +147,18 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "11b15112d348ca956ae061f53d51b37fcb0adb9b06f9b1245c4a93eddc08bb1f" +content-hash = "47104052627c5737e341ecfb58eb57c259f5baf5c99d7251dc860b331e18bee0" [metadata.files] atomicwrites = [ @@ -140,6 +177,55 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] +coverage = [ + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, +] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, @@ -164,7 +250,15 @@ pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, +] diff --git a/pyproject.toml b/pyproject.toml index a7b28dc..ea66bf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,23 @@ click = "^8.0" [tool.poetry.dev-dependencies] pytest = "^6.2" +coverage = {extras = ["toml"], version = "^6.2"} +pytest-cov = "^3.0.0" [tool.poetry.scripts] habitmove = "habitmove.cli:main" +[tool.coverage.paths] +source = ["src", "*/site-packages"] + +[tool.coverage.run] +branch = true +source = ["habitmove"] + +[tool.coverage.report] +show_missing = true +# fail_under = 80 # if we want pytest to automatically fail if not enough tests supplied + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/src/habitmove/loopdata.py b/src/habitmove/loopdata.py index 5d91a99..8b794f6 100644 --- a/src/habitmove/loopdata.py +++ b/src/habitmove/loopdata.py @@ -20,7 +20,6 @@ class Habit: position: int = 0 uuid: str = "" - # TODO test post init uuid setting def __post_init__(self): if not self.uuid or self.uuid == "": self.uuid = uuid4().hex diff --git a/tests/test_cli.py b/tests/test_cli.py index ffd50e3..0ab07a1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,10 +1,15 @@ import click.testing +import pytest from habitmove import cli -def test_cli_fails_without_file(): - runner = click.testing.CliRunner() +@pytest.fixture +def runner(): + return click.testing.CliRunner() + + +def test_cli_fails_without_file(runner): result = runner.invoke(cli.main) assert result.exit_code == 2 assert "Missing argument" in result.output From 8eb9b6a4928f3941fb665a530262e355e96c6909 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 7 Dec 2021 08:28:15 +0100 Subject: [PATCH 4/6] Add simple loop Habit uuid test --- tests/test_loopdata.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/test_loopdata.py diff --git a/tests/test_loopdata.py b/tests/test_loopdata.py new file mode 100644 index 0000000..c79a208 --- /dev/null +++ b/tests/test_loopdata.py @@ -0,0 +1,6 @@ +from habitmove.loopdata import Habit + + +def test_uuid_sets_automatically(): + sut = Habit(name="testhabit") + assert sut.uuid From aa2aff4e179121decb1afff91f753f43172ea786 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 7 Dec 2021 09:25:56 +0100 Subject: [PATCH 5/6] Fix import of nomie goal value --- src/habitmove/habits.py | 9 ++++-- tests/test_habits.py | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/test_habits.py diff --git a/src/habitmove/habits.py b/src/habitmove/habits.py index b7099c3..4a03e68 100644 --- a/src/habitmove/habits.py +++ b/src/habitmove/habits.py @@ -1,7 +1,10 @@ +import sqlite3 + +from habitmove.nomiedata import Tracker from habitmove.loopdata import Habit -def migrate(db, trackers): +def migrate(db: sqlite3.Connection, trackers: list[Tracker]): c = db.cursor() habits = trackers_to_habits(trackers) for habit in habits: @@ -34,7 +37,9 @@ def trackers_to_habits(trackers): habits[-1].type = 1 # nomie only has concept of max value, # use a percentage of it for Loop range target - habits[-1].target_value = int(t.max) // NOMIE_MAX_TO_TARGET_VALUE_RATIO + habits[-1].target_value = ( + t.goal or int(t.max) // NOMIE_MAX_TO_TARGET_VALUE_RATIO + ) return habits diff --git a/tests/test_habits.py b/tests/test_habits.py new file mode 100644 index 0000000..a015a14 --- /dev/null +++ b/tests/test_habits.py @@ -0,0 +1,66 @@ +import pytest +from habitmove import loopdata, nomiedata +from habitmove import habits + + +@pytest.fixture +def trackerlist(): + return [ + nomiedata.Tracker( + hidden=False, + score=-1, + tag="testtrack", + emoji="🧪", + label="Testing", + uom="kilotest", + id="12345", + ), + nomiedata.Tracker( + hidden=False, + tag="testtrack", + emoji="🧪", + label="Testing", + id="54321", + one_tap=False, + color="#FF0000", + ignore_zeros=False, + math="mean", + type="range", + uom="megatest", + min=0, + max=10, + goal=6, + default=2, + score=1, + ), + ] + + +def test_simple_habit_transform_from_tracker(trackerlist): + sut = trackerlist[0] + assert habits.trackers_to_habits([sut]) == [ + loopdata.Habit( + name="🧪 Testing", + description="testtrack", + unit="kilotest", + uuid="12345", + archived=False, + color=0, + ) + ] + + +def test_range_habit_transform_from_tracker(trackerlist): + sut = trackerlist[1] + assert habits.trackers_to_habits([sut]) == [ + loopdata.Habit( + name="🧪 Testing", + description="testtrack", + unit="megatest", + uuid="54321", + archived=False, + color=11, + type=1, + target_value=6, + ) + ] From b9c89155e346932cf338f9d146bd46bb1408d1b1 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 7 Dec 2021 09:32:26 +0100 Subject: [PATCH 6/6] Fix import of nomie numerical score values --- src/habitmove/habits.py | 2 +- src/habitmove/nomiedata.py | 33 +++++++++++++++++++-------------- tests/test_nomiedata.py | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 tests/test_nomiedata.py diff --git a/src/habitmove/habits.py b/src/habitmove/habits.py index 4a03e68..0866380 100644 --- a/src/habitmove/habits.py +++ b/src/habitmove/habits.py @@ -26,7 +26,7 @@ def trackers_to_habits(trackers): habits.append( Habit( archived=t.hidden, - color=11 if t.score != "-1" else 0, + color=0 if t.score == -1 else 11, description=t.tag, name=f"{t.emoji} {t.label}", unit="" if t.uom == "num" else t.uom, diff --git a/src/habitmove/nomiedata.py b/src/habitmove/nomiedata.py index cbcb9c7..9deab64 100644 --- a/src/habitmove/nomiedata.py +++ b/src/habitmove/nomiedata.py @@ -1,30 +1,35 @@ -from typing import Optional, Any +from typing import Any, Union from dataclasses import dataclass, field +import re # A nomie habit tracker. Tracks anything whose value can be encapsulated in a numerical value. @dataclass(frozen=True) class Tracker: - color: str - emoji: str - hidden: bool - id: str - ignore_zeros: bool - label: str - math: str - one_tap: bool tag: str - type: str - uom: str + label: str + id: str + one_tap: bool = True + color: str = "#000080" + emoji: str = "" + hidden: bool = False + ignore_zeros: bool = False + math: str = "mean" + type: str = "tick" # tick or range mostly + uom: str = "" # TODO no idea what include does - include: str + include: str = "" min: int = 0 max: int = 0 goal: int = 0 default: int = 0 - # TODO score can be string (if custom) or int (if simple good/bad) - score: str = "" + score: Union[int, str] = 1 # score can be string ('custom') or int score_calc: list[dict[str, Any]] = field(default_factory=lambda: []) + def __post_init__(self): + # ensure save as int if not 'custom' scoring + if re.match(r"^-?[0-9]+$", str(self.score)): + object.__setattr__(self, "score", int(self.score)) + @dataclass(frozen=True) class Activity: diff --git a/tests/test_nomiedata.py b/tests/test_nomiedata.py new file mode 100644 index 0000000..c2f9644 --- /dev/null +++ b/tests/test_nomiedata.py @@ -0,0 +1,18 @@ +from habitmove import nomiedata + + +def test_score_numerical_becomes_int(): + sut = nomiedata.Tracker(label="Int checking", tag="isint", id="1337", score="-1") + assert type(sut.score) == int + + +def test_score_invalid_int_stays_string(): + sut = nomiedata.Tracker(label="Int checking", tag="isint", id="1337", score="-1.3") + assert type(sut.score) == str + + +def test_score_string_stays_string(): + sut = nomiedata.Tracker( + label="Int checking", tag="isint", id="1337", score="custom" + ) + assert type(sut.score) == str