From 6f08e1cfcc0f486334624621d00f2cff758142b1 Mon Sep 17 00:00:00 2001 From: widal001 Date: Mon, 29 May 2023 11:54:58 -0400 Subject: [PATCH 01/22] build: Sets up poetry project for Milestone CLI --- milestone-cli/milestone_cli/__init__.py | 1 + milestone-cli/poetry.lock | 425 ++++++++++++++++++++++ milestone-cli/pyproject.toml | 19 + milestone-cli/tests/test_milestone_cli.py | 5 + 4 files changed, 450 insertions(+) create mode 100644 milestone-cli/milestone_cli/__init__.py create mode 100644 milestone-cli/poetry.lock create mode 100644 milestone-cli/pyproject.toml create mode 100644 milestone-cli/tests/test_milestone_cli.py diff --git a/milestone-cli/milestone_cli/__init__.py b/milestone-cli/milestone_cli/__init__.py new file mode 100644 index 000000000..3dc1f76bc --- /dev/null +++ b/milestone-cli/milestone_cli/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/milestone-cli/poetry.lock b/milestone-cli/poetry.lock new file mode 100644 index 000000000..867e32361 --- /dev/null +++ b/milestone-cli/poetry.lock @@ -0,0 +1,425 @@ +[[package]] +name = "black" +version = "23.3.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cachetools" +version = "5.3.1" +description = "Extensible memoizing collections and decorators" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "chardet" +version = "5.1.0" +description = "Universal encoding detector for Python 3" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx (>=6.1.3)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)", "pytest (>=7.3.1)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pathspec" +version = "0.11.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx (>=6.2.1)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest (>=7.3.1)"] + +[[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 = "pydantic" +version = "1.10.8" +description = "Data validation and settings management using python type hints" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pyproject-api" +version = "1.5.1" +description = "API to interact with the python pyproject.toml based projects" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +packaging = ">=23" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)", "sphinx (>=6.1.3)"] +testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest (>=7.2.1)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "ruff" +version = "0.0.270" +description = "An extremely fast Python linter, written in Rust." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tox" +version = "4.5.2" +description = "tox is a generic virtualenv management and test command line tool" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +cachetools = ">=5.3" +chardet = ">=5.1" +colorama = ">=0.4.6" +filelock = ">=3.12" +packaging = ">=23.1" +platformdirs = ">=3.5.1" +pluggy = ">=1" +pyproject-api = ">=1.5.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.23" + +[package.extras] +docs = ["furo (>=2023.5.20)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinx (>=7.0.1)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17)", "psutil (>=5.9.5)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.3.1)", "pytest (>=7.3.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] + +[[package]] +name = "typing-extensions" +version = "4.6.2" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "virtualenv" +version = "20.23.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.11,<4" +platformdirs = ">=3.2,<4" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx-argparse (>=0.4)", "sphinx (>=6.1.3)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage-enable-subprocess (>=1)", "coverage (>=7.2.3)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "pytest (>=7.3.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.10" +content-hash = "895db16e39ca95c51dfd42d49dd97216028c925896c1d58f563285dcd9546bf3" + +[metadata.files] +black = [ + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +] +cachetools = [ + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, +] +chardet = [ + {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, + {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +distlib = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] +exceptiongroup = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] +filelock = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] +iniconfig = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] +mypy-extensions = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] +packaging = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] +pathspec = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] +platformdirs = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +pydantic = [ + {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, + {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, + {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, + {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, + {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, + {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, + {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, + {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, + {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, +] +pyproject-api = [ + {file = "pyproject_api-1.5.1-py3-none-any.whl", hash = "sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"}, + {file = "pyproject_api-1.5.1.tar.gz", hash = "sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9"}, +] +pytest = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] +ruff = [ + {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, + {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"}, + {file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"}, + {file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"}, + {file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"}, + {file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tox = [ + {file = "tox-4.5.2-py3-none-any.whl", hash = "sha256:f1a9541b292aa0449f6c7bb67dc0073f25f9086413c3922fe47f5168cbf7b2f4"}, + {file = "tox-4.5.2.tar.gz", hash = "sha256:ad87fb7a10ef476afb6eb7e408808057f42976ef0d30ad5fe023099ba493ce58"}, +] +typing-extensions = [ + {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, + {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, +] +virtualenv = [ + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, +] diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml new file mode 100644 index 000000000..1b2a0268b --- /dev/null +++ b/milestone-cli/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "milestone-cli" +version = "0.1.0" +description = "" +authors = ["widal001 "] + +[tool.poetry.dependencies] +python = "^3.10" +pydantic = "^1.10.8" + +[tool.poetry.dev-dependencies] +pytest = "^7.3.1" +black = "^23.3.0" +ruff = "^0.0.270" +tox = "^4.5.2" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/milestone-cli/tests/test_milestone_cli.py b/milestone-cli/tests/test_milestone_cli.py new file mode 100644 index 000000000..c0ba94e56 --- /dev/null +++ b/milestone-cli/tests/test_milestone_cli.py @@ -0,0 +1,5 @@ +from milestone_cli import __version__ + + +def test_version(): + assert __version__ == "0.1.0" From 1a1e17dba3560d2bf7b39fbde38a3e523583e3b8 Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 30 May 2023 13:01:38 -0400 Subject: [PATCH 02/22] feat: Adds pydantic schemas for milestones These schemas will be used to parse the yaml file which contains the milestone summary as well as to generate the diagram and markdown files --- milestone-cli/milestone_cli/schemas.py | 69 +++++++++++++++ milestone-cli/tests/__init__.py | 0 milestone-cli/tests/test_schemas.py | 113 +++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 milestone-cli/milestone_cli/schemas.py create mode 100644 milestone-cli/tests/__init__.py create mode 100644 milestone-cli/tests/test_schemas.py diff --git a/milestone-cli/milestone_cli/schemas.py b/milestone-cli/milestone_cli/schemas.py new file mode 100644 index 000000000..0951cc71b --- /dev/null +++ b/milestone-cli/milestone_cli/schemas.py @@ -0,0 +1,69 @@ +from pydantic import BaseModel + + +class MilestoneBase(BaseModel): + """Contains fields shared by Milestone and Section""" + + heading: str + diagram_name: str + description: str + key: str | None = None + + +class Milestone(MilestoneBase): + """Contains a summary of details about a project milestone""" + + status: str + section: str | None = None + dependencies: list[str] | None = None + + +class MilestoneSection(MilestoneBase): + """Represents a group of project milestones""" + + milestones: dict[str, Milestone] + + +class Dependency(BaseModel): + """Documents a (downstream) milestone's dependency on an upstream milestone""" + + upstream: Milestone + downstream: Milestone + + +class MilestoneSummary(BaseModel): + """Stores the list of milestones and the sections they belong to""" + + version: str + sections: dict[str, MilestoneSection] + + def map_dependencies(self, milestone: Milestone) -> list[Dependency] | None: + """Split milestone dependencies into a list of Dependency objects""" + if not milestone.dependencies: + return None + deps = [] + for parent in milestone.dependencies: + upstream = self.milestones.get(parent) + if not upstream: + raise KeyError # TODO: Change to custom error type + deps.append(Dependency(upstream=upstream, downstream=milestone)) + return deps + + @property + def dependencies(self) -> list[Dependency]: + """Returns a list of Dependency objects across all milestones""" + results = [] + for milestone in self.milestones.values(): + dependencies = self.map_dependencies(milestone) + if dependencies: + results.extend(dependencies) + return results + + @property + def milestones(self) -> dict[str, Milestone]: + """Returns a combined list of Milestone objects""" + return { + milestone: details + for section in self.sections.values() + for milestone, details in section.milestones.items() + } diff --git a/milestone-cli/tests/__init__.py b/milestone-cli/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/milestone-cli/tests/test_schemas.py b/milestone-cli/tests/test_schemas.py new file mode 100644 index 000000000..2cd783586 --- /dev/null +++ b/milestone-cli/tests/test_schemas.py @@ -0,0 +1,113 @@ +from copy import deepcopy +import pytest + +from milestone_cli.schemas import ( + Dependency, + Milestone, + MilestoneSection, + MilestoneSummary, +) + + +@pytest.fixture(scope="module", name="summary") +def summary_fixture(): + """Creates a sample MilestoneSummary fixture for tests""" + sections = { + "project-goals": MilestoneSection( + heading="Project Goals", + diagram_name="Project Goals", + description="Project goals description", + milestones={ + "define-goals": Milestone( + heading="Define Goals", + diagram_name="Define-Goals", + description="Goals description", + status="executing", + ), + "measurement-strategy": Milestone( + key="measurement-strategy", + heading="Measurement Strategy", + diagram_name="Measurement-Strategy", + description="Measurement description", + status="executing", + dependencies=["define-goals"], + ), + }, + ), + "communications-tooling": MilestoneSection( + heading="Communications tooling", + diagram_name="Communications Tooling", + description="Project goals description", + milestones={ + "comms-platform": Milestone( + heading="Communications Platform", + diagram_name="Comms-Platform", + description="Comms platform description", + status="planning", + section="communications-tooling", + dependencies=["define-goals", "measurement-strategy"], + ), + }, + ), + } + return MilestoneSummary(version="0.1.0", sections=sections) + + +class TestDependencies: + """Tests the MilestoneSummary.dependencies hybrid property""" + + def test_returns_list_of_dependency_objects( + self, + summary: MilestoneSummary, + ) -> None: + """Should return a list of Depdency objects""" + # execution + output = summary.dependencies + # validation + assert isinstance(output[0], Dependency) + + def test_returns_correct_dependencies( + self, + summary: MilestoneSummary, + ) -> None: + """Should return a separate Dependency object for each dependency""" + # setup + goals = summary.milestones.get("define-goals") + measure = summary.milestones.get("measurement-strategy") + comms = summary.milestones.get("comms-platform") + expected = [ + Dependency(upstream=goals, downstream=measure), + Dependency(upstream=goals, downstream=comms), + Dependency(upstream=measure, downstream=comms), + ] + # execution + output = summary.dependencies + # validation + assert len(output) == len(expected) + for dependency in expected: + assert dependency in output + + def test_return_empty_list_if_no_deps( + self, + summary: MilestoneSummary, + ) -> None: + """Should return None if none of the milestones have dependencies""" + # setup - Set all dependencies to None + new_summary = deepcopy(summary) + for milestone in new_summary.milestones.values(): + milestone.dependencies = None + # execution + output = new_summary.dependencies + # validation + assert output == [] + + +class TestGetMilestonesBySection: + """Tests the MilestoneSummary.get_milestones_by_section() method""" + + def test_returns_list_of_milestones( + self, + summary: MilestoneSummary, + ) -> None: + """Should return a list of Milestone objects""" + assert 1 From 6135b6df3a45b33cf48a00c9ad379d73ef3d71d7 Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 30 May 2023 13:02:06 -0400 Subject: [PATCH 03/22] build: Adds pyyaml to project dependencies --- milestone-cli/poetry.lock | 45 +++++++++++++++++++++++++++++++++++- milestone-cli/pyproject.toml | 1 + 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/milestone-cli/poetry.lock b/milestone-cli/poetry.lock index 867e32361..ebf5d17ee 100644 --- a/milestone-cli/poetry.lock +++ b/milestone-cli/poetry.lock @@ -192,6 +192,14 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "ruff" version = "0.0.270" @@ -260,7 +268,7 @@ test = ["covdefaults (>=2.3)", "coverage-enable-subprocess (>=1)", "coverage (>= [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "895db16e39ca95c51dfd42d49dd97216028c925896c1d58f563285dcd9546bf3" +content-hash = "f9449ddbae9a73194c3a05aff9ce09e516b0caef213231617c2b11761fce8816" [metadata.files] black = [ @@ -388,6 +396,41 @@ pytest = [ {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, ] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] ruff = [ {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml index 1b2a0268b..4b939d1e0 100644 --- a/milestone-cli/pyproject.toml +++ b/milestone-cli/pyproject.toml @@ -7,6 +7,7 @@ authors = ["widal001 "] [tool.poetry.dependencies] python = "^3.10" pydantic = "^1.10.8" +PyYAML = "^6.0" [tool.poetry.dev-dependencies] pytest = "^7.3.1" From 88628d11094d13e171d493ea9831b7bf9003215a Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 30 May 2023 13:32:49 -0400 Subject: [PATCH 04/22] feat: Adds loads_milestones_from_yaml_file() This function loads a yaml file and returns a MilestoneSummary object --- milestone-cli/milestone_cli/schemas.py | 2 +- milestone-cli/milestone_cli/utils.py | 11 +++++ milestone-cli/tests/conftest.py | 3 ++ milestone-cli/tests/dummy_milestones.yaml | 53 +++++++++++++++++++++++ milestone-cli/tests/test_utils.py | 32 ++++++++++++++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 milestone-cli/milestone_cli/utils.py create mode 100644 milestone-cli/tests/conftest.py create mode 100644 milestone-cli/tests/dummy_milestones.yaml create mode 100644 milestone-cli/tests/test_utils.py diff --git a/milestone-cli/milestone_cli/schemas.py b/milestone-cli/milestone_cli/schemas.py index 0951cc71b..709b5577f 100644 --- a/milestone-cli/milestone_cli/schemas.py +++ b/milestone-cli/milestone_cli/schemas.py @@ -13,7 +13,7 @@ class MilestoneBase(BaseModel): class Milestone(MilestoneBase): """Contains a summary of details about a project milestone""" - status: str + status: str | None = None section: str | None = None dependencies: list[str] | None = None diff --git a/milestone-cli/milestone_cli/utils.py b/milestone-cli/milestone_cli/utils.py new file mode 100644 index 000000000..380aa91d8 --- /dev/null +++ b/milestone-cli/milestone_cli/utils.py @@ -0,0 +1,11 @@ +from pathlib import Path + +import yaml + +from milestone_cli.schemas import MilestoneSummary + + +def load_milestones_from_yaml_file(file_path: Path) -> MilestoneSummary: + with open(file_path) as f: + contents = yaml.safe_load(f) + return MilestoneSummary(**contents) diff --git a/milestone-cli/tests/conftest.py b/milestone-cli/tests/conftest.py new file mode 100644 index 000000000..8be41193e --- /dev/null +++ b/milestone-cli/tests/conftest.py @@ -0,0 +1,3 @@ +from pathlib import Path + +TEST_DIR = Path(__file__).absolute().parent diff --git a/milestone-cli/tests/dummy_milestones.yaml b/milestone-cli/tests/dummy_milestones.yaml new file mode 100644 index 000000000..5327d4640 --- /dev/null +++ b/milestone-cli/tests/dummy_milestones.yaml @@ -0,0 +1,53 @@ +version: "0.1.0" +sections: + project-goals: + heading: Project goals + description: Description of Auth milestones + diagram_name: Define goals + milestones: + define-goals: + status: complete + heading: Define and publish core project goals + diagram_name: Define-Goals + dependencies: null + description: | + Define and publish core project goals, with proposed key metrics for all goals. + + Define both short and long-term goals. + measurement-strategy: + status: planning + heading: Measurement strategy + diagram_name: Measurement-Strategy + dependencies: [define-goals] + description: Define and publish core project measurement strategy. + measurement-dashboard: + status: + heading: Public measurement dashboards + diagram_name: Measurement-Dashboard + dependencies: [measurement-strategy] + description: Launch initial public measurement dashboard. + initial-api-deployment: + heading: Initial API deployment milestones + description: Description of Auth milestones + diagram_name: Initial API deployment + milestones: + onboard-dev-team: + status: executing + heading: Onboard development team + diagram_name: Onboard-Dev-Team + dependencies: null + description: Have the software development team for this effort start their work and get systems access. + dev-tools: + status: not_started + heading: Developer tools + diagram_name: Dev-Tools + dependencies: [onboard-dev-team, measurement-strategies] + description: | + Install developer tools for backend, including: + + - Automated test framework + - Linters, code quality checkers, and code autoformatters + - Automated test coverage analysis + - API description linter (Spectral or alternative) - this can also be saved until API milestone + - Security scan of packages (Snyk or alternative) + - etc diff --git a/milestone-cli/tests/test_utils.py b/milestone-cli/tests/test_utils.py new file mode 100644 index 000000000..fffd8884d --- /dev/null +++ b/milestone-cli/tests/test_utils.py @@ -0,0 +1,32 @@ +import pytest + +from milestone_cli.utils import load_milestones_from_yaml_file, MilestoneSummary +from tests.conftest import TEST_DIR + +FILE_PATH = TEST_DIR / "dummy_milestones.yaml" + + +@pytest.fixture(scope="module", name="summary") +def loaded_summary_fixture() -> MilestoneSummary: + """Load the dummy_milestones.yaml file once for tests in this module""" + return load_milestones_from_yaml_file(FILE_PATH) + + +class TestLoadMilestonesFromYaml: + """Tests the load_milestones_from_yaml_file() function""" + + def test_parses_file_correctly(self, summary: MilestoneSummary) -> None: + """Should return a MilestoneSummary object with correct details""" + # validation + assert isinstance(summary, MilestoneSummary) + assert summary.version == "0.1.0" + assert len(summary.milestones) == 5 + + def test_milestones_order_preserved(self, summary: MilestoneSummary) -> None: + """Should preserve the order of milestones from the file""" + # setup + sections = list(summary.sections.values()) + milestones = list(sections[0].milestones.keys()) + # validation + assert milestones[0] == "define-goals" + assert milestones[2] == "measurement-dashboard" From 998c8bd36973b8e5a5b9ddbdcf5a38df9ef10500 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 11:55:18 -0400 Subject: [PATCH 05/22] build: Add jinja2 to project dependencies --- milestone-cli/poetry.lock | 80 +++++++++++++++++++++++++++++++++++- milestone-cli/pyproject.toml | 1 + 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/milestone-cli/poetry.lock b/milestone-cli/poetry.lock index ebf5d17ee..24ee845a8 100644 --- a/milestone-cli/poetry.lock +++ b/milestone-cli/poetry.lock @@ -94,6 +94,28 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -268,7 +290,7 @@ test = ["covdefaults (>=2.3)", "coverage-enable-subprocess (>=1)", "coverage (>= [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "f9449ddbae9a73194c3a05aff9ce09e516b0caef213231617c2b11761fce8816" +content-hash = "f5fc29635ad3267e123251add0ca9881aff797913677d35ca40e09f4b10d0144" [metadata.files] black = [ @@ -330,6 +352,62 @@ iniconfig = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] mypy-extensions = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml index 4b939d1e0..b6ec5b088 100644 --- a/milestone-cli/pyproject.toml +++ b/milestone-cli/pyproject.toml @@ -8,6 +8,7 @@ authors = ["widal001 "] python = "^3.10" pydantic = "^1.10.8" PyYAML = "^6.0" +Jinja2 = "^3.1.2" [tool.poetry.dev-dependencies] pytest = "^7.3.1" From eb1cd486fb56362eb613bb1574779ea5e43c7a76 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 11:56:40 -0400 Subject: [PATCH 06/22] feat: Adds render_jinja_template() function This function accepts a path to a jinja template and returns a string that has been populated with a series of parameters --- milestone-cli/README.rst | 0 milestone-cli/milestone_cli/schemas.py | 19 +++++++-- .../templates/milestone-summary.md | 39 +++++++++++++++++++ milestone-cli/milestone_cli/utils.py | 9 +++++ milestone-cli/tests/conftest.py | 5 ++- milestone-cli/tests/dummy_milestones.yaml | 2 +- milestone-cli/tests/test_schemas.py | 25 +++++++++--- milestone-cli/tests/test_utils.py | 32 +++++++++++++-- 8 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 milestone-cli/README.rst create mode 100644 milestone-cli/milestone_cli/templates/milestone-summary.md diff --git a/milestone-cli/README.rst b/milestone-cli/README.rst new file mode 100644 index 000000000..e69de29bb diff --git a/milestone-cli/milestone_cli/schemas.py b/milestone-cli/milestone_cli/schemas.py index 709b5577f..4d38df0ca 100644 --- a/milestone-cli/milestone_cli/schemas.py +++ b/milestone-cli/milestone_cli/schemas.py @@ -7,14 +7,12 @@ class MilestoneBase(BaseModel): heading: str diagram_name: str description: str - key: str | None = None class Milestone(MilestoneBase): """Contains a summary of details about a project milestone""" status: str | None = None - section: str | None = None dependencies: list[str] | None = None @@ -30,6 +28,13 @@ class Dependency(BaseModel): upstream: Milestone downstream: Milestone + def jinja_export(self) -> dict: + """Export just the diagram names for jinja templating""" + return { + "upstream": self.upstream.diagram_name, + "downstream": self.downstream.diagram_name, + } + class MilestoneSummary(BaseModel): """Stores the list of milestones and the sections they belong to""" @@ -45,7 +50,8 @@ def map_dependencies(self, milestone: Milestone) -> list[Dependency] | None: for parent in milestone.dependencies: upstream = self.milestones.get(parent) if not upstream: - raise KeyError # TODO: Change to custom error type + # TODO: Change to custom error type + raise KeyError(f"'{parent}' not found in list of milestones") deps.append(Dependency(upstream=upstream, downstream=milestone)) return deps @@ -67,3 +73,10 @@ def milestones(self) -> dict[str, Milestone]: for section in self.sections.values() for milestone, details in section.milestones.items() } + + def export_jinja_params(self) -> dict: + """Exports sections and milestones for use in jinja templates""" + return { + **self.dict(), + "dependencies": [dep.jinja_export() for dep in self.dependencies], + } diff --git a/milestone-cli/milestone_cli/templates/milestone-summary.md b/milestone-cli/milestone_cli/templates/milestone-summary.md new file mode 100644 index 000000000..c5333a5c1 --- /dev/null +++ b/milestone-cli/milestone_cli/templates/milestone-summary.md @@ -0,0 +1,39 @@ +{% for section in params.sections.values() %} +# {{ section.heading }} +{{ section.description }} + +{% for milestone, details in section.milestones.items() %} +## {{ details.heading }} + +| Property | Value | +| ------------------ | ------ | +| ID | `{{ milestone }}` | +| Status | {{ details.status }} | +| Diagram Short Name | `{{ details.diagram_name }}` | + +Dependencies: {{ milestone.dependencies }} + +{{ details.description }} +{% endfor -%} +{% endfor -%} + +# Appendix + +Here is a template to use for these descriptions. + +The first option below is for milestones that have not been defined according to the [Milestone Template](./milestone_template.md). It provides fields for a short description of the milestone. + +The second option below is for milestones that have already been defined according to the [Milestone Template](./milestone_template.md). It provides a link to the template-based definition. + +## Milestone name +Diagram short name: `-` + +Dependencies: `-` or `None` + +Short description. + +OR + +Diagram short name: `-` + +[Link]() diff --git a/milestone-cli/milestone_cli/utils.py b/milestone-cli/milestone_cli/utils.py index 380aa91d8..623e3831d 100644 --- a/milestone-cli/milestone_cli/utils.py +++ b/milestone-cli/milestone_cli/utils.py @@ -1,11 +1,20 @@ from pathlib import Path +import jinja2 import yaml from milestone_cli.schemas import MilestoneSummary def load_milestones_from_yaml_file(file_path: Path) -> MilestoneSummary: + """Load a yaml file and parse the contents into a MilestoneSummary object""" with open(file_path) as f: contents = yaml.safe_load(f) return MilestoneSummary(**contents) + + +def render_milestone_template(template_path: Path, params: str) -> str: + """""" + with open(template_path) as f: + template = jinja2.Template(f.read()) + return template.render(params=params) diff --git a/milestone-cli/tests/conftest.py b/milestone-cli/tests/conftest.py index 8be41193e..97d50ff97 100644 --- a/milestone-cli/tests/conftest.py +++ b/milestone-cli/tests/conftest.py @@ -1,3 +1,6 @@ from pathlib import Path -TEST_DIR = Path(__file__).absolute().parent +# Gets path to milestone-cli +ROOT_DIR = Path(__file__).absolute().parent.parent +TEST_DIR = ROOT_DIR / "tests" +TEMPLATE_DIR = ROOT_DIR / "milestone_cli" / "templates" diff --git a/milestone-cli/tests/dummy_milestones.yaml b/milestone-cli/tests/dummy_milestones.yaml index 5327d4640..05dcc279b 100644 --- a/milestone-cli/tests/dummy_milestones.yaml +++ b/milestone-cli/tests/dummy_milestones.yaml @@ -41,7 +41,7 @@ sections: status: not_started heading: Developer tools diagram_name: Dev-Tools - dependencies: [onboard-dev-team, measurement-strategies] + dependencies: [onboard-dev-team, measurement-strategy] description: | Install developer tools for backend, including: diff --git a/milestone-cli/tests/test_schemas.py b/milestone-cli/tests/test_schemas.py index 2cd783586..bc06aff9a 100644 --- a/milestone-cli/tests/test_schemas.py +++ b/milestone-cli/tests/test_schemas.py @@ -102,12 +102,27 @@ def test_return_empty_list_if_no_deps( assert output == [] -class TestGetMilestonesBySection: - """Tests the MilestoneSummary.get_milestones_by_section() method""" +@pytest.fixture(scope="class", name="params") +def jinja_params_fixture(summary: MilestoneSummary) -> dict: + """Export the summary once for the duration of the class""" + return summary.export_jinja_params() - def test_returns_list_of_milestones( + +class TestExportJinjaParams: + """Tests the MilestoneSummary.export_jinja_params() method""" + + def test_includes_dependencies_for_diagram( self, + params: dict, summary: MilestoneSummary, ) -> None: - """Should return a list of Milestone objects""" - assert 1 + """""" + # setup + dependencies = params.get("dependencies") + expected = { + "upstream": "Define-Goals", + "downstream": "Measurement-Strategy", + } + # validation + assert len(dependencies) == len(summary.dependencies) + assert dependencies[0] == expected diff --git a/milestone-cli/tests/test_utils.py b/milestone-cli/tests/test_utils.py index fffd8884d..884c950eb 100644 --- a/milestone-cli/tests/test_utils.py +++ b/milestone-cli/tests/test_utils.py @@ -1,15 +1,27 @@ import pytest -from milestone_cli.utils import load_milestones_from_yaml_file, MilestoneSummary -from tests.conftest import TEST_DIR +from milestone_cli.utils import ( + load_milestones_from_yaml_file, + render_milestone_template, + MilestoneSummary, +) +from tests.conftest import TEST_DIR, TEMPLATE_DIR -FILE_PATH = TEST_DIR / "dummy_milestones.yaml" +YAML_PATH = TEST_DIR / "dummy_milestones.yaml" +MARKDOWN_PATH = TEMPLATE_DIR / "milestone-summary.md" +MERMAID_PATH = TEMPLATE_DIR / "mermaid-diagram.mmd" @pytest.fixture(scope="module", name="summary") def loaded_summary_fixture() -> MilestoneSummary: """Load the dummy_milestones.yaml file once for tests in this module""" - return load_milestones_from_yaml_file(FILE_PATH) + return load_milestones_from_yaml_file(YAML_PATH) + + +@pytest.fixture(scope="class", name="params") +def jinja_params_fixture(summary: MilestoneSummary) -> dict: + """Export the summary once for the duration of the class""" + return summary.export_jinja_params() class TestLoadMilestonesFromYaml: @@ -30,3 +42,15 @@ def test_milestones_order_preserved(self, summary: MilestoneSummary) -> None: # validation assert milestones[0] == "define-goals" assert milestones[2] == "measurement-dashboard" + + +class TestRenderTemplate: + """Tests the render_milestone_template() function""" + + def test_milestone_summary_markdown(self, params) -> None: + """Tests rendering markdown file""" + # execution + output = render_milestone_template(MARKDOWN_PATH, params) + # validation + print(output) + assert 0 From d0f214c8606965eaf323e99d8c1ef4dad1953ee4 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 13:36:56 -0400 Subject: [PATCH 07/22] feat: Adds mermaid diagram template --- .../templates/milestone-diagram.mmd | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 milestone-cli/milestone_cli/templates/milestone-diagram.mmd diff --git a/milestone-cli/milestone_cli/templates/milestone-diagram.mmd b/milestone-cli/milestone_cli/templates/milestone-diagram.mmd new file mode 100644 index 000000000..871f0944a --- /dev/null +++ b/milestone-cli/milestone_cli/templates/milestone-diagram.mmd @@ -0,0 +1,38 @@ +%% A note on syntax: +%% 1. Since node IDs cannot have spaces, prefer to give each milestone a short name with any spaces replaced by `-`. For instance, "Development Tools Implemented" becomes "Dev-Tools". + +%% For unclear reasons, PyCharm's mermaid editor does not support title attributes. Comment on or off the title as needed. + +%% --- +%% title: Grants.gov modernization milestones +%% --- + +%% Diagram is oriented left-to-right ("LR") rather than top-to-bottom + +flowchart LR + + {% for section in params.sections.values() %} + subgraph {{ section.diagram_name -}} + {% for milestone in section.milestones.values() %} + {{ milestone.diagram_name }}:::{{ milestone.status if milestone.status else 'TODO' -}} + {% endfor %} + end + {% endfor %} + + subgraph Legend + direction LR + a1[This milestone is finished]:::finished --> a2[This milestone is being executed]:::executing + a3[This milestone is being planned]:::planning --> a4[This milestone is not yet planned] + a5[This milestone is a 'north star' milestone]:::northStar + end + + %% List relationships %% + {%- for dependency in params.dependencies %} + {{ dependency.upstream }} --> {{ dependency.downstream -}} + {% endfor %} + + %% Define some styles + classDef planning fill:#9999FF + classDef executing fill:#FF6633 + classDef finished fill:#99FF33 + classDef northStar fill:#cc99ff From ce945fe4add7e217739acb932de8af420c4f51e6 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 13:37:57 -0400 Subject: [PATCH 08/22] feat(utils): Adds create_or_replace_file() function --- milestone-cli/milestone_cli/utils.py | 14 +++++-- milestone-cli/tests/test_utils.py | 57 ++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/milestone-cli/milestone_cli/utils.py b/milestone-cli/milestone_cli/utils.py index 623e3831d..db3c649b4 100644 --- a/milestone-cli/milestone_cli/utils.py +++ b/milestone-cli/milestone_cli/utils.py @@ -14,7 +14,15 @@ def load_milestones_from_yaml_file(file_path: Path) -> MilestoneSummary: def render_milestone_template(template_path: Path, params: str) -> str: - """""" - with open(template_path) as f: - template = jinja2.Template(f.read()) + """Load and populate a milestone template with the parameters provided""" + template = jinja2.Template(template_path.read_text()) return template.render(params=params) + + +def create_or_replace_file(file_path: Path, new_contents: str) -> None: + """Create or replace a file with the contents provided""" + # create file and parent directory + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.touch(exist_ok=True) + # overwrite contents of the file + file_path.write_text(new_contents) diff --git a/milestone-cli/tests/test_utils.py b/milestone-cli/tests/test_utils.py index 884c950eb..936025e4a 100644 --- a/milestone-cli/tests/test_utils.py +++ b/milestone-cli/tests/test_utils.py @@ -1,15 +1,18 @@ +from pathlib import Path import pytest +import re from milestone_cli.utils import ( load_milestones_from_yaml_file, render_milestone_template, + create_or_replace_file, MilestoneSummary, ) from tests.conftest import TEST_DIR, TEMPLATE_DIR YAML_PATH = TEST_DIR / "dummy_milestones.yaml" MARKDOWN_PATH = TEMPLATE_DIR / "milestone-summary.md" -MERMAID_PATH = TEMPLATE_DIR / "mermaid-diagram.mmd" +MERMAID_PATH = TEMPLATE_DIR / "milestone-diagram.mmd" @pytest.fixture(scope="module", name="summary") @@ -48,9 +51,55 @@ class TestRenderTemplate: """Tests the render_milestone_template() function""" def test_milestone_summary_markdown(self, params) -> None: - """Tests rendering markdown file""" + """Checks that the milestone-summary.md template renders correctly""" # execution output = render_milestone_template(MARKDOWN_PATH, params) - # validation print(output) - assert 0 + # validation - number of sections and milestones + assert len(re.findall("\n# ", output)) == 3 # 2 sections and appendix + assert len(re.findall("\n## ", output)) == 6 # 5 milestones and appendix + + def test_milestone_mermaid_diagram(self, params) -> None: + """Checks that the milestone-diagram.mmd template renders correctly""" + # execution + output = render_milestone_template(MERMAID_PATH, params) + # validation - number of subgraphs and dependencies + assert len(re.findall("subgraph", output)) == 3 # 2 sections and legend + assert len(re.findall("--> ", output)) == 6 # 4 milestones and 2 in legend + + +class TestCreateOrReplaceFile: + """Tests the create_or_replace_file() function""" + + CONTENTS = """ + # Heading 1 + ### Heading 3 + """ + + def test_create_file_if_it_does_not_exist(self, tmp_path: Path): + """The output file should be created if it does not exist""" + # setup + output = tmp_path / "output.md" + assert output.exists() is False + # execution + create_or_replace_file(output, new_contents=self.CONTENTS) + # validation + assert output.exists() is True + assert output.read_text() == self.CONTENTS + + def test_replace_contents_of_existing_file(self, tmp_path: Path): + """The previous contents of the output file should be replaced""" + # setup -- create file + output = tmp_path / "output.md" + output.touch(exist_ok=True) + assert output.exists() is True + # setup -- write contents to be replaced + old_contents = "Remove this text" + output.write_text(old_contents) + output.read_text() == old_contents + # execution + create_or_replace_file(output, new_contents=self.CONTENTS) + # validation + new_contents = output.read_text() + assert old_contents not in new_contents + assert new_contents == self.CONTENTS From fe7303a1c5acb0e669bee8efd4b994759e034bdd Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 13:48:36 -0400 Subject: [PATCH 09/22] fix: default status for milestones --- milestone-cli/milestone_cli/templates/milestone-summary.md | 2 +- milestone-cli/tests/dummy_milestones.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/milestone-cli/milestone_cli/templates/milestone-summary.md b/milestone-cli/milestone_cli/templates/milestone-summary.md index c5333a5c1..6d6cb25c1 100644 --- a/milestone-cli/milestone_cli/templates/milestone-summary.md +++ b/milestone-cli/milestone_cli/templates/milestone-summary.md @@ -8,7 +8,7 @@ | Property | Value | | ------------------ | ------ | | ID | `{{ milestone }}` | -| Status | {{ details.status }} | +| Status | {{ details.status if details.status else 'TODO' }} | | Diagram Short Name | `{{ details.diagram_name }}` | Dependencies: {{ milestone.dependencies }} diff --git a/milestone-cli/tests/dummy_milestones.yaml b/milestone-cli/tests/dummy_milestones.yaml index 05dcc279b..ff0052747 100644 --- a/milestone-cli/tests/dummy_milestones.yaml +++ b/milestone-cli/tests/dummy_milestones.yaml @@ -6,7 +6,7 @@ sections: diagram_name: Define goals milestones: define-goals: - status: complete + status: finished heading: Define and publish core project goals diagram_name: Define-Goals dependencies: null @@ -38,7 +38,7 @@ sections: dependencies: null description: Have the software development team for this effort start their work and get systems access. dev-tools: - status: not_started + status: heading: Developer tools diagram_name: Dev-Tools dependencies: [onboard-dev-team, measurement-strategy] From 58baaf534182903262fb90c7b060ab06e1e96f32 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 15:28:24 -0400 Subject: [PATCH 10/22] build: Adds typer to project dependencies Typer supports building simple but powerful CLI tools --- milestone-cli/poetry.lock | 28 +++++++++++++++++++++++++--- milestone-cli/pyproject.toml | 18 +++++++++++------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/milestone-cli/poetry.lock b/milestone-cli/poetry.lock index 24ee845a8..280c64159 100644 --- a/milestone-cli/poetry.lock +++ b/milestone-cli/poetry.lock @@ -40,7 +40,7 @@ python-versions = ">=3.7" name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -51,7 +51,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" @@ -262,6 +262,24 @@ virtualenv = ">=20.23" docs = ["furo (>=2023.5.20)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinx (>=7.0.1)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17)", "psutil (>=5.9.5)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.3.1)", "pytest (>=7.3.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)", "rich (>=10.11.0,<14.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "pillow (>=9.3.0,<10.0.0)", "cairosvg (>=2.5.2,<3.0.0)"] +test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "coverage (>=6.2,<7.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)", "rich (>=10.11.0,<14.0.0)"] + [[package]] name = "typing-extensions" version = "4.6.2" @@ -290,7 +308,7 @@ test = ["covdefaults (>=2.3)", "coverage-enable-subprocess (>=1)", "coverage (>= [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "f5fc29635ad3267e123251add0ca9881aff797913677d35ca40e09f4b10d0144" +content-hash = "2eeaf092810b25acff4ead4b806b3a0d81f1e88333cc4e43bf90fbd3239a65e5" [metadata.files] black = [ @@ -536,6 +554,10 @@ tox = [ {file = "tox-4.5.2-py3-none-any.whl", hash = "sha256:f1a9541b292aa0449f6c7bb67dc0073f25f9086413c3922fe47f5168cbf7b2f4"}, {file = "tox-4.5.2.tar.gz", hash = "sha256:ad87fb7a10ef476afb6eb7e408808057f42976ef0d30ad5fe023099ba493ce58"}, ] +typer = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] typing-extensions = [ {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml index b6ec5b088..4b06cc55a 100644 --- a/milestone-cli/pyproject.toml +++ b/milestone-cli/pyproject.toml @@ -1,21 +1,25 @@ [tool.poetry] +authors = ["widal001 "] +description = "" name = "milestone-cli" version = "0.1.0" -description = "" -authors = ["widal001 "] [tool.poetry.dependencies] -python = "^3.10" -pydantic = "^1.10.8" -PyYAML = "^6.0" Jinja2 = "^3.1.2" +PyYAML = "^6.0" +pydantic = "^1.10.8" +python = "^3.10" +typer = "^0.9.0" [tool.poetry.dev-dependencies] -pytest = "^7.3.1" black = "^23.3.0" +pytest = "^7.3.1" ruff = "^0.0.270" tox = "^4.5.2" [build-system] -requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0"] + +[tool.poetry.scripts] +milestone-cli = "milestone_cli.cli:app" From f385a014781dd848f59c9aa338e8d46f999f2684 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 15:29:11 -0400 Subject: [PATCH 11/22] fix: How dependencies rendered in summary template --- .../milestone_cli/templates/milestone-summary.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/milestone-cli/milestone_cli/templates/milestone-summary.md b/milestone-cli/milestone_cli/templates/milestone-summary.md index 6d6cb25c1..ef8b9ed38 100644 --- a/milestone-cli/milestone_cli/templates/milestone-summary.md +++ b/milestone-cli/milestone_cli/templates/milestone-summary.md @@ -8,10 +8,16 @@ | Property | Value | | ------------------ | ------ | | ID | `{{ milestone }}` | -| Status | {{ details.status if details.status else 'TODO' }} | +| Status | {{ details.status if details.status else "TODO" }} | | Diagram Short Name | `{{ details.diagram_name }}` | -Dependencies: {{ milestone.dependencies }} +{% if details.dependencies %} +Dependencies: {% for dependency in details.dependencies -%} +`{{ dependency }}`{{", " if not loop.last else "" }} +{%- endfor %} +{%- else %} +Dependencies: None +{% endif %} {{ details.description }} {% endfor -%} From 11ab465e70dd67ef11be2088b27e6b466a1bf9c4 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 15:32:00 -0400 Subject: [PATCH 12/22] feat: Add CLI entrypoints --- milestone-cli/milestone_cli/cli.py | 79 ++++++++++++++++++++++++++++++ milestone-cli/pyproject.toml | 2 +- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 milestone-cli/milestone_cli/cli.py diff --git a/milestone-cli/milestone_cli/cli.py b/milestone-cli/milestone_cli/cli.py new file mode 100644 index 000000000..7bca54671 --- /dev/null +++ b/milestone-cli/milestone_cli/cli.py @@ -0,0 +1,79 @@ +from pathlib import Path + +import typer + +from milestone_cli.utils import ( + load_milestones_from_yaml_file, + render_milestone_template, + create_or_replace_file, + MilestoneSummary, +) + +# path to root of the repository +REPO_ROOT_DIR = Path(__file__).absolute().parent.parent.parent + +# path to project directories and files +PROJECT_ROOT = REPO_ROOT_DIR / "milestone-cli" +TEMPLATE_DIR = PROJECT_ROOT / "milestone_cli" / "templates" +DIAGRAM_TEMPLATE = TEMPLATE_DIR / "milestone-diagram.mmd" +SUMMARY_TEMPLATE = TEMPLATE_DIR / "milestone-summary.md" + +# external directories and files +MILESTONE_DIR = REPO_ROOT_DIR / "documentation" / "milestones" +MILESTONE_FILE = MILESTONE_DIR / "milestone-summaries.yaml" + + +app = typer.Typer(name="Milestone CLI") + + +@app.command(name="hello") +def hello_world(name: str | None = None): + """Say hello to a given name or Hello World by default""" + print(f"Hello {name if name else 'world'}") + + +@app.command(name="validate") +def validate_yaml_file_contents( + yaml_file: str | None = None, +) -> MilestoneSummary: + """Loads MilestoneSummary from yaml file and validates contents""" + if not yaml_file: + file_path = MILESTONE_FILE + else: + file_path = Path(yaml_file).absolute() + if not file_path.exists(): + print(f"No file found at {file_path}") + return + if file_path.suffix not in (".yaml", ".yml"): + print(f"{file_path} is not a path to a yaml file") + return + milestones = load_milestones_from_yaml_file(file_path) + print("Everything looks good") + return milestones + + +@app.command(name="populate") +def populate_output_file( + kind: str, + output_file: str, + yaml_file: str | None = None, +) -> None: + """Populate either the diagram or the summary file""" + if kind == "diagram": + template_path = DIAGRAM_TEMPLATE + elif kind == "summary": + template_path = SUMMARY_TEMPLATE + else: + print("Command must either be 'populate summary' or 'populate diagram'") + # load milestones and get output path + milestones = validate_yaml_file_contents(yaml_file) + file_path = Path(output_file).absolute() + print(f"Writing {kind} to {file_path}") + # create or replace output file with rendered template + create_or_replace_file( + file_path=file_path, + new_contents=render_milestone_template( + template_path, + milestones.export_jinja_params(), + ), + ) diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml index 4b06cc55a..e5cc7f007 100644 --- a/milestone-cli/pyproject.toml +++ b/milestone-cli/pyproject.toml @@ -22,4 +22,4 @@ build-backend = "poetry.core.masonry.api" requires = ["poetry-core>=1.0.0"] [tool.poetry.scripts] -milestone-cli = "milestone_cli.cli:app" +milestone = "milestone_cli.cli:app" From 4f439b364b85fbffd60a17491ebf55841dfa2a7c Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 15:33:01 -0400 Subject: [PATCH 13/22] feat: Creates a yaml version of the milestone summary This provides a single source of truth for milestones in the repo --- .../milestones/milestone-summaries.yaml | 682 ++++++++++++++++++ 1 file changed, 682 insertions(+) create mode 100644 documentation/milestones/milestone-summaries.yaml diff --git a/documentation/milestones/milestone-summaries.yaml b/documentation/milestones/milestone-summaries.yaml new file mode 100644 index 000000000..003386e41 --- /dev/null +++ b/documentation/milestones/milestone-summaries.yaml @@ -0,0 +1,682 @@ +version: "0.1.0" +sections: + project-goals: + heading: Project goals + description: Description of Auth milestones + diagram_name: Define goals + milestones: + define-goals: + status: northStar + heading: Define and publish core project goals + diagram_name: Define-Goals + dependencies: null + description: | + Define and publish core project goals, with proposed key metrics for all goals. + + Define both short and long-term goals. + measurement-strategy: + status: + heading: Measurement strategy + diagram_name: Measurement-Strategy + dependencies: [define-goals] + description: Define and publish core project measurement strategy. + measurement-dashboard: + status: + heading: Public measurement dashboards + diagram_name: Measurement-Dashboard + dependencies: [measurement-strategy] + description: Launch initial public measurement dashboard. + initial-api-deployment: + heading: Initial API deployment milestones + description: Description of Auth milestones + diagram_name: Initial API deployment + milestones: + onboard-dev-team: + status: planning + heading: Onboard development team + diagram_name: Onboard-Dev-Team + dependencies: null + description: Have the software development team for this effort start their work and get systems access. + dev-tools: + status: + heading: Developer tools + diagram_name: Dev-Tools + dependencies: null + description: | + Install developer tools for backend, including: + + - Automated test framework + - Linters, code quality checkers, and code autoformatters + - Automated test coverage analysis + - API description linter (Spectral or alternative) - this can also be saved until API milestone + - Security scan of packages (Snyk or alternative) + - etc + db-replica: + status: + heading: DB replica + diagram_name: DB-Replica + dependencies: null + description: > + Create a replica of AWS PROD database for legacy grants.gov in a beta.grants.gov environment. + Replica should (1) journal all changes from legacy DB to new system DB and (2) support uptime + even while legacy DB is undergoing planned or unplanned downtime, maintenance, and upgrades. + + The creation of this replica should be scripted using infrastructure as code. + db-test-data: + status: + heading: Test data and schema + diagram_name: DB-Test-Data + dependencies: [db-replica] + description: | + Create a script or other automated tool that: + + 1. Generates a database matching the schema of the beta.grants.gov core replica DB. + 2. Generates consistent fake example data that can be used for testing. (e.g., using the same random seed to generate consistent data, or saving test data values somewhere accessible) + + This will dramatically speed up development because it will allow any developer to generate a local copy of the database that acts the same as the PROD beta.grants.gov database in their environment. + + By having consistent test data, we can create integration tests that look for expected and unexpected behaviors. + saas-plan: + status: + heading: Review of software-as-a-service (SaaS) alternatives + diagram_name: SaaS-Plan + dependencies: [onboard-dev-team] + description: TODO add description + db-api-plan: + status: + heading: DB & API planning + diagram_name: DB-API-Plan + dependencies: [saas-plan] + description: TODO add description + api-docs-plan: + status: + heading: Serialization and API documentation planning + diagram_name: API-Docs-Plan + dependencies: [db-api-plan] + description: TODO add description + get-opportunities: + status: northStar + heading: GET opportunities + diagram_name: GET-Opportunities + dependencies: + [db-api-plan, db-test-data, db-replica, dev-tools, beta-domain] + description: TODO add description + beta-domain: + status: + heading: beta.grants.gov domain + diagram_name: Beta-Domain + dependencies: null + description: TODO add description + feature-flags: + status: + heading: Feature flag framework + diagram_name: Feature-Flags + dependencies: [onboard-dev-team] + description: TODO add description + api-versioning: + status: + heading: API versioning framework + diagram_name: API-Versioning + dependencies: [db-api-plan] + description: TODO add description + performance-testing: + status: + heading: Performance testing framework + diagram_name: Performance-Testing + dependencies: [db-api-plan] + description: TODO add description + ato: + status: + heading: ATO + diagram_name: ATO + dependencies: [db-api-plan] + description: TODO add description + ci-cd: + status: + heading: CI-CD + diagram_name: CI-CD + dependencies: [db-api-plan, ato] + description: TODO add description + ci-cd-metrics: + status: + heading: CI-CD-Metrics + diagram_name: CI-CD-Metrics + dependencies: [ci-cd] + description: TODO add description + api-docs: + status: + heading: API documentation + diagram_name: API-Docs + dependencies: [api-docs-plan, get-opportunities, beta-domain] + description: TODO add description + webhooks-opportunities: + status: + heading: Webhooks for opportunities + diagram_name: Webhooks-Opportunities + dependencies: [db-api-plan, get-opportunities] + description: TODO add description + dependency-fundraising-tracking: + status: + heading: Back-end dependency fundraising tracking + diagram_name: Dependency-Fundraising-Tracking + dependencies: [db-api-plan] + description: TODO add description + opportunity-protocol: + status: + heading: Develop opportunity protocol + diagram_name: Opportunity-Protocol + dependencies: [db-api-plan] + description: TODO add description + communications-tooling: + heading: Communications Tooling + description: Description of Auth milestones + diagram_name: Communications Tooling + milestones: + comms-platforms: + status: + heading: Communication platforms + diagram_name: Comms-Platforms + dependencies: null + description: TODO add description + milestone-template: + status: executing + heading: Milestone template published + diagram_name: Milestone-Template + dependencies: null + description: TODO add description + open-source-tools: + status: + heading: Open source tools + diagram_name: Open-Source-Tools + dependencies: [comms-platforms] + description: TODO add description + open-source-group: + status: northStar + heading: Open source group kickoff + diagram_name: Open-Source-Group + dependencies: [open-source-tools, comms-platforms] + description: TODO add description + bug-bounty: + status: + heading: Bug bounty + diagram_name: Bug-Bounty + dependencies: null + description: TODO add description + setup-meetings: + status: + heading: Setup recurring meetings + diagram_name: Setup-Meetings + dependencies: null + description: TODO add description + nofo: + heading: NOFO milestones + description: Description of Auth milestones + diagram_name: NOFO + milestones: + nofo-milestone-planning: + status: executing + heading: NOFO milestone planning + diagram_name: NOFO-Milestone-Planning + dependencies: null + description: TODO add description + nofo-prototypes: + status: planning + heading: NOFO prototypes + diagram_name: NOFO-prototypes + dependencies: null + description: TODO add description + nofo-prototypes-metrics: + status: + heading: Define usability test metrics for NOFO prototypes + diagram_name: NOFO-Prototypes-Metrics + dependencies: null + description: TODO add description + nofo-prototypes-learnings: + status: + heading: Report on learnings from NOFO prototypes + diagram_name: NOFO-Prototypes-Learnings + dependencies: [nofo-prototypes-metrics, nofo-prototypes] + description: TODO add description + nofos-readability-scoring: + status: + heading: Readability scoring for NOFOs + diagram_name: NOFOs-Readability-Scoring + dependencies: [nofos-text] + description: TODO add description + nofos-writing-assistance-plan: + status: planning + heading: "Writing assistance tools for NOFOs: planning stage" + diagram_name: NOFOs-Writing-Assistance-Plan + dependencies: null + description: TODO add description + nofos-writing-assistance: + status: + heading: "Writing assistance tools for NOFOs: implementation stage" + diagram_name: NOFOs-Writing-Assistance + dependencies: [nofos-writing-assistance-plan] + description: TODO add description + start-pra-comment: + status: planning + heading: PRA submitted & public comment started + diagram_name: Start-PRA-Comment + dependencies: null + description: TODO add description + analytics: + heading: Analytics milestones + description: Description of Auth milestones + diagram_name: Communications Tooling + milestones: + analytics-api: + status: + heading: Operational analytics endpoint + diagram_name: Analytics-API + dependencies: [define-goals, get-opportunities] + description: TODO add description + data-quality-api: + status: + heading: Data quality analytics endpoint + diagram_name: Data-Quality-API + dependencies: [analytics-api] + description: TODO add description + analytics-visualization: + status: northStar + heading: Data analytics visualization + diagram_name: Analytics-Visualization + dependencies: [measurement-dashboard, analytics-api, data-quality-api] + description: TODO add description + place-of-performance-api: + status: + heading: Publish place of performance data + diagram_name: Place-Of-Performance-API + dependencies: [analytics-api] + description: TODO add description + place-of-performance-visualization: + status: northStar + heading: Place of performance analytics visualization + diagram_name: Place-Of-Performance-Visualization + dependencies: [place-of-performance-api] + description: TODO add description + auth: + heading: Auth milestones + description: Description of Auth milestones + diagram_name: AuthN and AuthZ + milestones: + document-authn-authz: + status: + heading: Document authentication (authN) and authorization (authZ) frameworks + diagram_name: Document-AuthN-AuthZ + dependencies: null + description: TODO add description + authn-api: + status: + heading: Authenticate via API using Login.gov + diagram_name: AuthN-API + dependencies: [get-opportunities, document-authn-authz] + description: TODO add description + authz-api: + status: + heading: Authorize via API + diagram_name: AuthZ-API + dependencies: [authn-api, document-authn-authz] + description: TODO add description + oauth: + status: + heading: OAuth + diagram_name: OAuth + dependencies: [authn-api] + description: TODO add description + entity-api: + status: northStar + heading: User and entity information endpoint + diagram_name: Entity-API + dependencies: [authn-api] + description: TODO add description + sam-gov-integration: + heading: Sam.gov integration milestones + description: Description of Auth milestones + diagram_name: Sam.gov integration + milestones: + document-sam: + status: + heading: Document sam.gov tasks + diagram_name: Document-Sam + dependencies: null + description: TODO add description + sam-apis: + status: northStar + heading: Integrate sam.gov APIs + diagram_name: Sam-APIs + dependencies: [document-sam] + description: TODO add description + sam-help-desk: + status: + heading: Integrate sam.gov and grants.gov help desk + diagram_name: Sam-Help-Desk + dependencies: [document-sam] + description: TODO add description + hackathon: + heading: Hackathon milestones + description: Description of Auth milestones + diagram_name: Hackathon + milestones: + hackathon-page: + status: + heading: Hackathon informational page + diagram_name: Hackathon-Page + dependencies: null + description: TODO add description + recruit-hackathon-participants: + status: + heading: Recruit hackathon participants + diagram_name: Recruit-Hackathon-Participants + dependencies: null + description: TODO add description + recruit-hackathon-workgroups: + status: + heading: Recruit hackathon workgroups + diagram_name: Recruit-Hackathon-Workgroups + dependencies: null + description: TODO add description + rfi: + status: + heading: Request for information (RFI) + diagram_name: RFI + dependencies: null + description: TODO add description + rfi-tools: + status: + heading: Request for information (RFI) tooling + diagram_name: RFI-Tools + dependencies: null + description: TODO add description + hackathon-listing: + status: northStar + heading: "Hackathon Phase 1: NOFO listing" + diagram_name: Hackathon-Listing + dependencies: + [ + open-source-group, + hackathon-page, + rfi, + nofo-prototypes, + recruit-hackathon-workgroups, + recruit-hackathon-participants, + ] + description: TODO add description + hackathon-listing-internal-entry: + status: + heading: "Hackathon NOFO listing: Internal entry" + diagram_name: hackathon-listing-Internal-Entry + dependencies: [hackathon-listing, research-synthesis] + description: TODO add description + hackathon-apply: + status: + heading: "Hackathon Phase 2: Apply" + diagram_name: Hackathon-Apply + dependencies: [hackathon-listing] + description: TODO add description + hackathon-apply-internal-entry: + status: + heading: "Hackathon NOFO Apply: Internal entry" + diagram_name: Hackathon-Apply-Internal-Entry + dependencies: [hackathon-apply, research-synthesis] + description: TODO add description + hackathon-data: + status: + heading: "Hackathon Phase 3: Data" + diagram_name: Hackathon-Data + dependencies: [hackathon-apply, analytics-api] + description: TODO add description + hackathon-data-internal-entry: + status: + heading: "Hackathon data: Internal entry" + diagram_name: Hackathon-Data-Internal-Entry + dependencies: [hackathon-data] + description: TODO add description + user-interface: + heading: User interface milestones + description: Description of Auth milestones + diagram_name: User interface + milestones: + fe-plan: + status: + heading: Front-end planning + diagram_name: FE-Plan + dependencies: null + description: TODO add description + fe-ci-cd: + status: + heading: Front-end CI-CD + diagram_name: FE-CI-CD + dependencies: [fe-plan] + description: TODO add description + cms: + status: + heading: CMS + diagram_name: CMS + dependencies: [fe-plan] + description: TODO add description + cms-open-source: + status: + heading: Open source CMS content + diagram_name: CMS-Open-Source + dependencies: [cms] + description: TODO add description + web-analytics: + status: + heading: Web analytics + diagram_name: Web-Analytics + dependencies: [fe-plan] + description: TODO add description + web-analytics-legacy: + status: + heading: Grants.gov analytics + diagram_name: Web-Analytics-Legacy + dependencies: null + description: TODO add description + i18n: + status: + heading: Internationalization framework + diagram_name: i18n + dependencies: [fe-plan, cms] + description: TODO add description + translation-process: + status: northStar + heading: Translation process & contracts + diagram_name: Translation-Process + dependencies: [i18n, cms-open-source] + description: TODO add description + foundational-ui: + status: northStar + heading: Foundational UI + diagram_name: Foundational-UI + dependencies: [fe-ci-cd, research-synthesis] + description: TODO add description + search: + heading: Search milestones + description: Description of Auth milestones + diagram_name: Search + milestones: + search-api: + status: + heading: Search API + diagram_name: Search-API + dependencies: [get-opportunities] + description: TODO add description + search-ui: + status: northStar + heading: Search UI + diagram_name: Search-UI + dependencies: [search-api, foundational-ui] + description: TODO add description + nofos-text: + status: + heading: Full text of NOFOs stored as text + diagram_name: NOFOs-Text + dependencies: [get-opportunities] + description: TODO add description + nofos-text-search: + status: + heading: Full text search of NOFOs + diagram_name: NOFOs-Text-Search + dependencies: [nofos-text, search-api] + description: TODO add description + search-api-2.0: + status: + heading: Tuned search API (Search 2.0) + diagram_name: Search-API-2.0 + dependencies: [nofos-text-search] + description: TODO add description + geographic-search: + status: + heading: Geographic search + diagram_name: Geographic-Search + dependencies: [search-api] + description: TODO add description + search-user-research: + status: + heading: User research for search + diagram_name: Search-User-Research + dependencies: [search-api] + description: TODO add description + additional-search: + status: + heading: Additional search + diagram_name: Additional-Search + dependencies: [search-user-research] + description: TODO add description + personalization: + heading: Personalization milestones + description: Description of Auth milestones + diagram_name: Personalization + milestones: + save-nofos: + status: + heading: Save NOFOs to profile + diagram_name: Save-NOFOs + dependencies: [entity-api] + description: TODO add description + nofos-worked: + status: + heading: Show NOFOs the user has worked on + diagram_name: NOFOs-Worked + dependencies: [entity-api] + description: TODO add description + nofo-recommendations-api: + status: northStar + heading: NOFO Recommendations API + diagram_name: NOFO-Recommendations-API + dependencies: [save-nofos, nofos-worked, profile-builder] + description: TODO add description + nofo-recommendations-ui: + status: + heading: NOFO Recommendations UI + diagram_name: NOFO-Recommendations-UI + dependencies: [nofo-recommendations-api, search-ui] + description: TODO add description + profile-builder: + status: + heading: Profile builder + diagram_name: Profile-Builder + dependencies: [entity-api] + description: TODO add description + user-research: + heading: User research milestones + description: Description of Auth milestones + diagram_name: User-Research + milestones: + generative-user-research: + status: finished + heading: Generative user research + diagram_name: Generative-User-Research + dependencies: null + description: TODO add description + research-synthesis: + status: executing + heading: Research synthesis and vision + diagram_name: Research-synthesis + dependencies: [generative-user-research] + description: TODO add description + document-grantsolutions: + status: + heading: Document GrantSolutions integration points + diagram_name: Document-GrantSolutions + dependencies: null + description: TODO add description + user-research-compensation: + status: + heading: User research compensation + diagram_name: User-Research-Compensation + dependencies: null + description: TODO add description + user-research-database: + status: + heading: User research participants database + diagram_name: User-Research-Database + dependencies: null + description: TODO add description + user-research-tbd: + status: + heading: Additional user research TBD + diagram_name: User-Research-TBD + dependencies: [research-synthesis] + description: TODO add description + user-research-nofo-writing: + status: + heading: NOFO writing journey mapping + diagram_name: User-Research-NOFO-Writing + dependencies: [research-synthesis] + description: TODO add description + unified-brand: + status: + heading: Unified cross-platform branding and identity + diagram_name: Unified-Brand + dependencies: [research-synthesis] + description: TODO add description + expanded-ui: + heading: Expanded UI milestones + description: Description of Auth milestones + diagram_name: User-Research + milestones: + nofo-listing-ui: + status: + heading: NOFO Listing page + diagram_name: NOFO-Listing-UI + dependencies: [foundational-ui, research-synthesis] + description: TODO add description + nofo-listing-ui-enhanced: + status: northStar + heading: Enhanced NOFO Listing page + diagram_name: NOFO-Listing-UI-Enhanced + dependencies: [nofo-listing-ui, user-research-nofo-writing] + description: TODO add description + sam-assistance-ui: + status: + heading: Sam.gov assistance page + diagram_name: Sam-Assistance-UI + dependencies: [foundational-ui, document-sam, research-synthesis] + description: TODO add description + grants-assistance-ui: + status: + heading: Grants.gov assistance page + diagram_name: Grants-Assistance-UI + dependencies: [foundational-ui, research-synthesis] + description: TODO add description + help-ui: + status: + heading: Help page + diagram_name: Help-UI + dependencies: [foundational-ui, research-synthesis] + description: TODO add description + authn-ui: + status: + heading: Authentication (authN) page + diagram_name: AuthN-UI + dependencies: [foundational-ui, authn-api, research-synthesis] + description: TODO add description + authz-ui: + status: + heading: Authorization (authZ) page + diagram_name: AuthZ-UI + dependencies: [foundational-ui, authz-api, research-synthesis] + description: TODO add description From 48a5cdeb8431ed96d77845b97cdcfcaeb3923b40 Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 17:11:42 -0400 Subject: [PATCH 14/22] docs: Fills out README.md --- milestone-cli/README.md | 50 ++++++++++++++++++++++++++++++++++++ milestone-cli/README.rst | 0 milestone-cli/pyproject.toml | 2 +- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 milestone-cli/README.md delete mode 100644 milestone-cli/README.rst diff --git a/milestone-cli/README.md b/milestone-cli/README.md new file mode 100644 index 000000000..dde908662 --- /dev/null +++ b/milestone-cli/README.md @@ -0,0 +1,50 @@ +# Milestone CLI Tool + +The milestone CLI tool streamlines the process of validating and publishing changes to the milestone summary yaml file. + +## Getting Started + +### Prerequisites + +The following items are pre-requisites for installing this CLI tool: + +- Python version 3.10 or greater +- Poetry + +Validate that these requirements are met with: +```shell +python --version +poetry --version +``` + +### Quickstart + +1. Install the package: `poetry install` +2. Activate a shell: `poetry shell` +3. Validate the milestone yaml file: `milestones validate` +4. Populate the files: + - Diagram: `milestones populate diagram ./diagram.mmd` + - Summary: `milestones populate summary ./summary.md` +5. Exit the shell session: `exit` + + +## Made with + +- Project Dependencies + - pydantic - Extends python dataclasses for data (de)serialization and validation + - typer - Enables building simple but powerful command-line interfaces + - Jinja2 - Templating engine used to populate documents with data + - PyYAML - (De)serializes data between yaml files and python objects +- Dev Dependencies + - poetry - Python build tool and dependency manager + - ruff - Fast python linter built in Rust + - black - Auto-formatting for python + - pytest - Unit test framework + - tox - Test and linting orchestrator + + +## Roadmap + +- Simplify setup with Makefile +- Expand validations that are run on load +- Improve exception handling and error messages diff --git a/milestone-cli/README.rst b/milestone-cli/README.rst deleted file mode 100644 index e69de29bb..000000000 diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml index e5cc7f007..09b1da15e 100644 --- a/milestone-cli/pyproject.toml +++ b/milestone-cli/pyproject.toml @@ -22,4 +22,4 @@ build-backend = "poetry.core.masonry.api" requires = ["poetry-core>=1.0.0"] [tool.poetry.scripts] -milestone = "milestone_cli.cli:app" +milestones = "milestone_cli.cli:app" From 07d7bb7adb13f25d4177e713cc43e0484a20237f Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 18:26:12 -0400 Subject: [PATCH 15/22] ci: Runs pre-commit on all files --- README.md | 2 +- documentation/milestones/milestone-summaries.yaml | 4 ++-- milestone-cli/README.md | 2 +- repolinter.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 081e9d6de..d0018868a 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ A modernization effort for Grants.gov. 1. Visit [localhost:9001](https://localhost:9001) to view the server --> -### Setting up development tools +### Setting up development tools #### Configuring pre-commit hooks diff --git a/documentation/milestones/milestone-summaries.yaml b/documentation/milestones/milestone-summaries.yaml index 003386e41..46e60bf8a 100644 --- a/documentation/milestones/milestone-summaries.yaml +++ b/documentation/milestones/milestone-summaries.yaml @@ -57,8 +57,8 @@ sections: diagram_name: DB-Replica dependencies: null description: > - Create a replica of AWS PROD database for legacy grants.gov in a beta.grants.gov environment. - Replica should (1) journal all changes from legacy DB to new system DB and (2) support uptime + Create a replica of AWS PROD database for legacy grants.gov in a beta.grants.gov environment. + Replica should (1) journal all changes from legacy DB to new system DB and (2) support uptime even while legacy DB is undergoing planned or unplanned downtime, maintenance, and upgrades. The creation of this replica should be scripted using infrastructure as code. diff --git a/milestone-cli/README.md b/milestone-cli/README.md index dde908662..ef6e6698a 100644 --- a/milestone-cli/README.md +++ b/milestone-cli/README.md @@ -9,7 +9,7 @@ The milestone CLI tool streamlines the process of validating and publishing chan The following items are pre-requisites for installing this CLI tool: - Python version 3.10 or greater -- Poetry +- Poetry Validate that these requirements are met with: ```shell diff --git a/repolinter.json b/repolinter.json index b13ae611d..916a11f9b 100644 --- a/repolinter.json +++ b/repolinter.json @@ -364,4 +364,4 @@ } } } -} \ No newline at end of file +} From a21182cfeb64806526c8171149e990e0ad6f300a Mon Sep 17 00:00:00 2001 From: widal001 Date: Wed, 31 May 2023 18:29:22 -0400 Subject: [PATCH 16/22] build: Adds .vscode/ to .gitingore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 22bc4790d..cc17fa711 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,7 @@ dmypy.json # Pyre type checker .pyre/ + + +# VSCode settings +.vscode/ From 91b2d4f5fb74332638e2e7309c2b7059ef695fed Mon Sep 17 00:00:00 2001 From: widal001 Date: Sun, 4 Jun 2023 12:41:07 -0400 Subject: [PATCH 17/22] feat: Finishes adding milestones to yaml file --- .../milestones/milestone-summaries.yaml | 587 +++++++++++++++--- 1 file changed, 484 insertions(+), 103 deletions(-) diff --git a/documentation/milestones/milestone-summaries.yaml b/documentation/milestones/milestone-summaries.yaml index 46e60bf8a..28a2261d5 100644 --- a/documentation/milestones/milestone-summaries.yaml +++ b/documentation/milestones/milestone-summaries.yaml @@ -19,13 +19,15 @@ sections: heading: Measurement strategy diagram_name: Measurement-Strategy dependencies: [define-goals] - description: Define and publish core project measurement strategy. + description: | + Define and publish core project measurement strategy. measurement-dashboard: status: heading: Public measurement dashboards diagram_name: Measurement-Dashboard dependencies: [measurement-strategy] - description: Launch initial public measurement dashboard. + description: | + Launch initial public measurement dashboard. initial-api-deployment: heading: Initial API deployment milestones description: Description of Auth milestones @@ -36,7 +38,8 @@ sections: heading: Onboard development team diagram_name: Onboard-Dev-Team dependencies: null - description: Have the software development team for this effort start their work and get systems access. + description: | + Have the software development team for this effort start their work and get systems access. dev-tools: status: heading: Developer tools @@ -45,21 +48,19 @@ sections: description: | Install developer tools for backend, including: - - Automated test framework - - Linters, code quality checkers, and code autoformatters - - Automated test coverage analysis - - API description linter (Spectral or alternative) - this can also be saved until API milestone - - Security scan of packages (Snyk or alternative) - - etc + - Automated test framework + - Linters, code quality checkers, and code autoformatters + - Automated test coverage analysis + - API description linter (Spectral or alternative) - this can also be saved until API milestone + - Security scan of packages (Snyk or alternative) + - etc db-replica: status: heading: DB replica diagram_name: DB-Replica dependencies: null - description: > - Create a replica of AWS PROD database for legacy grants.gov in a beta.grants.gov environment. - Replica should (1) journal all changes from legacy DB to new system DB and (2) support uptime - even while legacy DB is undergoing planned or unplanned downtime, maintenance, and upgrades. + description: | + Create a replica of AWS PROD database for legacy grants.gov in a beta.grants.gov environment. Replica should (1) journal all changes from legacy DB to new system DB and (2) support uptime even while legacy DB is undergoing planned or unplanned downtime, maintenance, and upgrades. The creation of this replica should be scripted using infrastructure as code. db-test-data: @@ -81,92 +82,194 @@ sections: heading: Review of software-as-a-service (SaaS) alternatives diagram_name: SaaS-Plan dependencies: [onboard-dev-team] - description: TODO add description + description: | + Make an analysis of needs and alternatives and try to identify whether any aspects of the grants.gov experience would be better suited to using software-as-a-service (SaaS) alternatives such as Salesforce, Shopify, or other frameworks rather than developing a custom build. + + Write an analysis of alternatives with pros/cons/recommendations. db-api-plan: status: heading: DB & API planning diagram_name: DB-API-Plan dependencies: [saas-plan] - description: TODO add description + description: | + Make an analysis of needs and alternatives and choose: + + * Database(s) type(s) (e.g., relational, document store, or alternative) + * Database(s) language(s) (e.g., MongoDB, Postgres or alternative) + * Database(s) deployment(s) (e.g., RDS or alternative) + * Database ORM (e.g., SQLAlchemy or alternative) + * API types (e.g., RESTful JSON, webhooks, GraphQL, or alternatives) + * API language (e.g., Python, Node) + * API deployment (e.g., EC2, serverless, or alternative) api-docs-plan: status: heading: Serialization and API documentation planning diagram_name: API-Docs-Plan dependencies: [db-api-plan] - description: TODO add description + description: | + Make an analysis of needs and alternatives and choose: + + * Serialization framework (e.g., Marshmallow or alternative) + * API autogenerator + * API documentation (e.g., OpenAPI) autogenerator + + Ideally, fields would be defined only once in the codebase, and these definitions would be used to generate (with as little manual configuration as possible) both RESTful API endpoints and the documentation for those endpoints. + + Otherwise, every time there is a change in schema, it requires a lot of developer attention to update field definitions, API definitions, and API documentation. get-opportunities: status: northStar heading: GET opportunities diagram_name: GET-Opportunities dependencies: [db-api-plan, db-test-data, db-replica, dev-tools, beta-domain] - description: TODO add description + description: | + Deploy a public endpoint to PROD that allows users to see at least one (but not necessarily more than one!) field of data per listed opportunity in grants.gov. This will probably be a RESTful JSON /GET API endpoint. + + This API should be accessible even when legacy grants.gov is experiencing planned or unplanned outages. + + Metrics: + * Number of unique users accessing API + * Number of total API calls made + * Error rate of API calls + * Uptime of service + * etc. beta-domain: status: heading: beta.grants.gov domain diagram_name: Beta-Domain dependencies: null - description: TODO add description + description: | + Gain access to the subdomain, `beta.grants.gov`. Prove access by deploying static test content (e.g., ipsum lorem) to that subdomain. feature-flags: status: heading: Feature flag framework diagram_name: Feature-Flags dependencies: [onboard-dev-team] - description: TODO add description + description: | + Choose and implement framework for having feature flags in production. api-versioning: status: heading: API versioning framework diagram_name: API-Versioning dependencies: [db-api-plan] - description: TODO add description + description: | + Based on the plan defined in `DB-API-Plan`, choose and implement a framework for versioning the API using semantic versioning and being able to easily issue minor and major changes to the API. + + Document versioning in API docs. + + Also develop and execute release management plan with plan for release cycles, release notes, etc. performance-testing: status: heading: Performance testing framework diagram_name: Performance-Testing dependencies: [db-api-plan] - description: TODO add description + description: | + Setup and start running performance testing framework for testing systems under load. ato: status: heading: ATO diagram_name: ATO dependencies: [db-api-plan] - description: TODO add description + description: | + Review planned public deployment of services with Jacob, our security officer, to confirm that they all fall under our existing ATO for grants.gov ci-cd: status: heading: CI-CD diagram_name: CI-CD dependencies: [db-api-plan, ato] - description: TODO add description + description: | + Based on the plan defined in `DB-API-Plan`, choose and implement a framework for continuous integration and continuous deployment. + + When commits are made to any branch, these should automatically: + * Run lint suite + * Run test suite + * Deploy branch to backend and/or frontend test environments + + When commits are made to `staging` branch, these should automatically: + * Deploy branch to STAGING environments + * Run smoke tests and integration tests in STAGING + + When merges are made to `main`, these should automatically: + * Deploy branch to PROD environments + * Run smoke tests and integration tests in PROD + + Metrics: + * Frequency of deploys to production + * Duration of time it takes per deploy + * Duration of time it takes per rollback + + For discussion: is a STAGING environment and `staging` branch necessary at this stage, or should we continuously deploy to PROD as feature branches are merged into `main`? + + Ideally, we would like to ensure a certain percentage of our ongoing sprint velocity (at least 20-30% at the beginning?) is devoted to improving our developer tools (i.e., autoformatters, build tools, etc) and communication tools (i.e., our wiki, milestones docs, etc). By investing in speeding up team productivity, we will drastically increase the speed of our delivery. + + We should track the percentage of story points devoted to improving team efficiency and joy and ensure that they're hitting at least our target percentage each sprint. ci-cd-metrics: status: heading: CI-CD-Metrics diagram_name: CI-CD-Metrics dependencies: [ci-cd] - description: TODO add description + description: | + Design and implement public metrics on CI-CD, such as the average speed of running a full test suite and deployment. + + Try to keep these speeds down as much as possible: developer speed determines the speed of our feedback loops, and fast feedback loops are how we stay responsive to user needs and keep site performance high. api-docs: status: heading: API documentation diagram_name: API-Docs dependencies: [api-docs-plan, get-opportunities, beta-domain] - description: TODO add description + description: | + Based on the plan defined in `API-Docs-Plan`, launch a publicly accessible page for developer tooling and API documentation, such as https://beta.grants.gov/developers. For reference, see examples such as the one at https://qpp.cms.gov/developers. + + This could be a microsite hosted on its own, or deployed within and as part of the beta.grants.gov integrated front-end experience. + + This should include full OpenAPI documentation of the `GET Opportunities` API, as well as a tutorial for users who are interacting with the `beta.grants.gov` APIs for the first time. + + The documentation should also describe what is likely to be stable in the API as it is developed versus what is unstable and will likely change without warning. + + The update and deployment of documentation should be fully open source and managed through infrastructure as code. + + Configure web analytics in documentation to track visitors. + + Metrics: + + * Number of unique monthly average users of API documentation + * Number of repeat users of API documentation + * Feedback survey results on usability of API documentation? webhooks-opportunities: status: heading: Webhooks for opportunities diagram_name: Webhooks-Opportunities dependencies: [db-api-plan, get-opportunities] - description: TODO add description + description: | + Deploy a public webhook to PROD that allows users to subscribe to receive system-to-system updates when data is updated in the beta.grants.gov database. + + The definition of this milestone could change based on planning done in `DB-API-Plan`. + + Metrics: + * Number of unique users receiving webhooks calls + * Number of total webhooks calls received dependency-fundraising-tracking: status: heading: Back-end dependency fundraising tracking diagram_name: Dependency-Fundraising-Tracking dependencies: [db-api-plan] - description: TODO add description + description: | + For the front-end, there is a great tool (https://backyourstack.com/) that shows fundraising being conducted by the packages that are used as dependencies in your project. This tool currently only supports fundraising done through OpenCollective, which provides a systematic API that can be used to collect this data. + + This milestone is to add to that tool support for backend packages such as Python that are raising funds through OpenCollective. + + Based on initial research, the level of effort should be assessed to determine whether the time it would take to implement this milestone is worth the result. If it's longer than 2 weeks time of one developer, it may not be worth it. opportunity-protocol: status: heading: Develop opportunity protocol diagram_name: Opportunity-Protocol dependencies: [db-api-plan] - description: TODO add description + description: | + Investigate whether it is appropriate to develop a protocol that is system-agnostic that describes the core components of what's contained in an opportunity. This could be shared across both federal grantmaking as well as some private-sector grantmaking to facilitate standardization and simplification. This could help bring us closer to the goal of having, for grantmaking, a version of what US colleges and universities share in their [Common App](https://www.commonapp.org/). + + The protocol would be implemented by our particular API ecosystem, but other platforms could implement the same protocol as well. + + Much more investigation is needed to define this milestone. communications-tooling: heading: Communications Tooling description: Description of Auth milestones @@ -177,37 +280,110 @@ sections: heading: Communication platforms diagram_name: Comms-Platforms dependencies: null - description: TODO add description + description: | + [Link](./individual_milestones/communication_platforms.md) milestone-template: status: executing heading: Milestone template published diagram_name: Milestone-Template dependencies: null - description: TODO add description + description: | + [Link](./individual_milestones/milestone_template_published.md) open-source-tools: status: heading: Open source tools diagram_name: Open-Source-Tools dependencies: [comms-platforms] - description: TODO add description + description: | + Configure the following things in the open source repository: + + 1. License + 2. Responsible disclosure policy + 3. Wiki (or link to wiki) + 4. Contributions guide + 5. Anti-deficiency act explainer + 6. Paperwork reduction act explainer open-source-group: status: northStar heading: Open source group kickoff diagram_name: Open-Source-Group dependencies: [open-source-tools, comms-platforms] - description: TODO add description + description: | + Configure the following things for the open source group: + + 1. Chat channel for communication + 2. Unconference tools for voting on agenda topics (EasyRetro or alternative) + 3. Mailing list (Google Group, GSA List, or alternative) + + Then schedule and execute a kickoff meeting with interested attendees. bug-bounty: status: heading: Bug bounty diagram_name: Bug-Bounty dependencies: null - description: TODO add description + description: | + Start executing an ongoing bug bounty that provides a means for security researchers to disclose their findings in a manner additive to our existing responsible disclosure policy. setup-meetings: status: heading: Setup recurring meetings diagram_name: Setup-Meetings dependencies: null - description: TODO add description + description: | + Setup ongoing meetings that are necessary for delivery across all teams working on grants.gov and NOFO simplification. + + These meetings will help us align with a two-week sprint cycle. As much as possible, meetings will be open to members of the open source community and general public. + + Where possible and appropriate, it would be great to use 'unconference' tooling (the CEJST team used GitHub issues, but this had a challenging user experience) so that any participant can propose a topic for discussion for either themselves or others to facilitate. On the CEJST team, the team would propose a topic subject, description, duration, and facilitator name. For instance, here's an example entry: + + Subject: Review latest user research findings + Description: We just completed two weeks of our latest user research study and want to review the findings and answer questions for anyone interested + Duration: 30 minutes + Facilitator: Abdul + + Ideally these topics would have updates as well to let participants indicate which presentations they're most interested in seeing. + + As of this time, it probably makes most sense to have all these meetings online, or at least in a hybrid online/in-person format. + + These should be discussed and refined, and we're open to all ideas about how to make this more effective, but here's an example set of meetings: + + 1. Team of teams: sprint planning (60m, once per 2 weeks) + - Review delivery plan for each team for the coming 2 week sprint + - This meeting is at the start of the sprint. + 2. Team of teams: burndown check-in (30m, once per 2 weeks) + - Mid-sprint review of burndown charts against delivery. + - This meeting is in the middle of the sprint. + 3. Demo of demos (60m, once per 2 weeks) + - All teams present demos of what was delivered this sprint. + 4. Retro (60m, once per 2 weeks) + - Retrospective on what went well, what could go better, review and agree upon specific ideas for improvements + - Using a tool such as EasyRetro + - At the end of the agenda, each person self-reports how happy they are with their work and the project right now, and what could be done to make them happier. + 5. Open discussion calendar holds (60m, once per two weeks) + - Calendar hold to keep time on everyone's calendar for whatever ad-hoc topics are needed. + - Use unconference tooling + 6. Milestone pre-planning (15m, as needed) + - One meeting per milestone definition + - Do a quick overview of milestones that are getting ready to be defined + 7. Milestone planning (60m, as needed) + - One meeting per milestone definition + - Present near-final draft of milestone definition, according to milestone template. + - Review and get feedback from all attendees, answer all outstanding questions + - Mark milestone as fully defined and ready to be worked. + 8. Grants.gov tea time (60m, once per two weeks) + - A casual social hang to get to know each other better as people outside of just our daily work. + - Host this during a casual part of the sprint cycle (not the beginning or end). + 9. Open source community gathering (60m, once per two weeks) + - A gathering of members of the public specifically to talk about issues relevant to the open source nature of the project. + + To organize the delivery of this milestone, it may be useful to create a ticket for each of the meetings underneath the overall milestone epic. + + For each of these meetings, their definition of done is: + + * Meeting invites sent + * Method for easily adding people to the invites is documented publicly + * Recurring agenda set (in our shared comms tools (e.g., our Wiki) and in body of the calendar invite) + * Roles are set (e.g., an MC or rotating MC schedule is assigned) + * We've had at least one of these meetings nofo: heading: NOFO milestones description: Description of Auth milestones @@ -218,49 +394,57 @@ sections: heading: NOFO milestone planning diagram_name: NOFO-Milestone-Planning dependencies: null - description: TODO add description + description: | + Document ongoing milestones related to NOFO simplification. nofo-prototypes: status: planning heading: NOFO prototypes diagram_name: NOFO-prototypes dependencies: null - description: TODO add description + description: | + Deliver 4 NOFOs where the text and the PDF layout of the NOFO are fully redesigned. nofo-prototypes-metrics: status: heading: Define usability test metrics for NOFO prototypes diagram_name: NOFO-Prototypes-Metrics dependencies: null - description: TODO add description + description: | + Define usability testing metrics that will be used to evaluate the success of the 4 NOFO prototypes. nofo-prototypes-learnings: status: heading: Report on learnings from NOFO prototypes diagram_name: NOFO-Prototypes-Learnings dependencies: [nofo-prototypes-metrics, nofo-prototypes] - description: TODO add description + description: | + Create report defining what we've learned from NOFO prototypes usability testing, such as a clear set of best practices and things to avoid. nofos-readability-scoring: status: heading: Readability scoring for NOFOs diagram_name: NOFOs-Readability-Scoring dependencies: [nofos-text] - description: TODO add description + description: | + Automatically run readability scoring on all NOFOs posted to Grants.gov. nofos-writing-assistance-plan: status: planning heading: "Writing assistance tools for NOFOs: planning stage" diagram_name: NOFOs-Writing-Assistance-Plan dependencies: null - description: TODO add description + description: | + Plan for how to provide grantors with tools that integrate into their daily workflow that will help them write simpler, clearer NOFOs. nofos-writing-assistance: status: heading: "Writing assistance tools for NOFOs: implementation stage" diagram_name: NOFOs-Writing-Assistance dependencies: [nofos-writing-assistance-plan] - description: TODO add description + description: | + Implement writing assistance tools. start-pra-comment: status: planning heading: PRA submitted & public comment started diagram_name: Start-PRA-Comment dependencies: null - description: TODO add description + description: | + Submit generic clearance documents to secure generic PRA approval. The approval does not need to be completed to complete the milestone, but the documents do need to be submitted and the public comment period needs to be started by posting them publicly. analytics: heading: Analytics milestones description: Description of Auth milestones @@ -271,31 +455,38 @@ sections: heading: Operational analytics endpoint diagram_name: Analytics-API dependencies: [define-goals, get-opportunities] - description: TODO add description + description: | + Produce one statistic about the operational effectiveness of grants.gov that is served with live data over a public API endpoint. This can be any statistic. Preferably it should be something useful that is interesting to members of the HHS team and members of the public, such as the total number of applications received for a given opportunity. data-quality-api: status: heading: Data quality analytics endpoint diagram_name: Data-Quality-API dependencies: [analytics-api] - description: TODO add description + description: | + Produce one statistic about the data quality of one or more fields in grants.gov that is served with live data over a public API endpoint. This can be any statistic. Preferably it should be something useful that is interesting to members of the HHS team and members of the public, such as the percent of null values in a given field, reported by Agency/OpDiv. analytics-visualization: status: northStar heading: Data analytics visualization diagram_name: Analytics-Visualization dependencies: [measurement-dashboard, analytics-api, data-quality-api] - description: TODO add description + description: | + Produce visualizations of the data served over the two endpoints. These visualizations should be publicly accessible and scripted so that they are updated frequently and/or at the time of a visitor landing on the page. They should not be produced using one-off techniques but should be part of a sustainable, deployed analytics solution. + + This could look like Javascript code being used to write and deploy D3 visualizations, or Python notebooks that are hosted in such a fashion that the data powering the visualizations is updated live. place-of-performance-api: status: heading: Publish place of performance data diagram_name: Place-Of-Performance-API dependencies: [analytics-api] - description: TODO add description + description: | + Produce an API endpoint that for at least two grantmaking programs provides data on the place of performance of their grants. This will be using spatial data. place-of-performance-visualization: status: northStar heading: Place of performance analytics visualization diagram_name: Place-Of-Performance-Visualization dependencies: [place-of-performance-api] - description: TODO add description + description: | + Produce visualizations of the place of performance data. This could look like a map using MapBox or MapLibre (preferable because it's open source). auth: heading: Auth milestones description: Description of Auth milestones @@ -306,31 +497,40 @@ sections: heading: Document authentication (authN) and authorization (authZ) frameworks diagram_name: Document-AuthN-AuthZ dependencies: null - description: TODO add description + description: | + Document the authentication (authN) and authorization (authZ) frameworks currently used by grants.gov with as much specificity and accuracy as possible. What workflows do users go through to access new authZ roles? What permissions do they get per role? + + Furthermore, we should also document with as much specificity and accuracy as possible the authN and authZ roles of sam.gov, since completing tasks in sam.gov is necessary for completing most tasks within grants.gov. authn-api: status: heading: Authenticate via API using Login.gov diagram_name: AuthN-API dependencies: [get-opportunities, document-authn-authz] - description: TODO add description + description: | + Users should be able to login to an authenticated experience of the APIs using their login.gov credentials. This will at the moment not provide any additional functionality to users (login will be the basis of future functionality). authz-api: status: heading: Authorize via API diagram_name: AuthZ-API dependencies: [authn-api, document-authn-authz] - description: TODO add description + description: | + Users should be able to manage authorizations (authZ) for their grants.gov work over APIs. oauth: status: heading: OAuth diagram_name: OAuth dependencies: [authn-api] - description: TODO add description + description: | + Users should be able to delegate API access by logging in using their login.gov credentials to another system, which authenticates with beta.grants.gov APIs on their behalf using OAuth. + + OAuth is already implemented as part of login.gov: https://developers.login.gov/oidc/. entity-api: status: northStar heading: User and entity information endpoint diagram_name: Entity-API dependencies: [authn-api] - description: TODO add description + description: | + Authenticated users should be able to see at least one field about their profile (for instance, whether their affiliated entity has a UEI number already affiliated). sam-gov-integration: heading: Sam.gov integration milestones description: Description of Auth milestones @@ -341,19 +541,28 @@ sections: heading: Document sam.gov tasks diagram_name: Document-Sam dependencies: null - description: TODO add description + description: | + Document all tasks that need to be completed on sam.gov to enable functionality on grants.gov. + + Document which of these tasks can be completed using APIs versus which must go through the sam.gov user interface. sam-apis: status: northStar heading: Integrate sam.gov APIs diagram_name: Sam-APIs dependencies: [document-sam] - description: TODO add description + description: | + Integrate with the [Sam.gov APIs](https://open.gsa.gov/api/entity-api/) to complete as many Sam.gov-related tasks as possible through grants.gov. + + Much more definition of this milestone is needed. sam-help-desk: status: heading: Integrate sam.gov and grants.gov help desk diagram_name: Sam-Help-Desk dependencies: [document-sam] - description: TODO add description + description: | + Investigate potentailly providing a "warm hand-off" option to integrate sam.gov and grants.gov help desks to reduce user frustration at being told to hang up and call a different help desk. + + This may not be feasible. hackathon: heading: Hackathon milestones description: Description of Auth milestones @@ -364,31 +573,54 @@ sections: heading: Hackathon informational page diagram_name: Hackathon-Page dependencies: null - description: TODO add description + description: | + Deploy a webpage somewhere that gives a full description of the hackathon plan, invitation, signup, etc. + + As part of this milestone, choose a better and more inclusive name than "hackathon". recruit-hackathon-participants: status: heading: Recruit hackathon participants diagram_name: Recruit-Hackathon-Participants dependencies: null - description: TODO add description + description: | + Recruit at least 3 organizations that have said they are interested in participating in the first hackathon phase on NOFO listings. recruit-hackathon-workgroups: status: heading: Recruit hackathon workgroups diagram_name: Recruit-Hackathon-Workgroups dependencies: null - description: TODO add description + description: | + For the hackathon, we may choose to form workgroups that function as a sort of caucus that makes recommendations about various entries. This paradigm can be continued as part of the ongoing open source work on grants.gov. + + Workgroups review demos and make recommendations and endorsements representing their affinity group. + + For instance, there could be different workgroups for: + + * Staff from city governments + * Environmental justice / Justice40 organizations + * Grantmaking networks (such as Grant Professionals Association) + * Visually impaired individuals and/or individuals with other accessibility needs + * System-to-system users + + Grants.gov team staff would provide support through tools to assist these groups in decisionmaking and submitting recommendations. As with all members of the public, they'd be invited to attend sprint demos and other planning sessions. + + For this milestone, we would recruit at least three participants for each of three workgroups to help get the concept started. + + Recruit at least 3 organizations that have said they are interested in participating in the first hackathon phase on NOFO listings. rfi: status: heading: Request for information (RFI) diagram_name: RFI dependencies: null - description: TODO add description + description: | + Begin a request for information (RFI) process to solicit public comment on NOFO design and simplicity. rfi-tools: status: heading: Request for information (RFI) tooling diagram_name: RFI-Tools dependencies: null - description: TODO add description + description: | + Configure and deploy better tools for soliciting and processing responses from the request for information (RFI) process. hackathon-listing: status: northStar heading: "Hackathon Phase 1: NOFO listing" @@ -402,37 +634,56 @@ sections: recruit-hackathon-workgroups, recruit-hackathon-participants, ] - description: TODO add description + description: | + Target date: aim to kick off this hackathon by March 2024 at the latest + + Phase 1 of the hackathon: the listing itself. + + Key parts of this milestone: + + * Large invite list + * Kickoff event (hybrid in-person and remote) + * Speakers and introductions organized for kickoff event + * Demo 4 simplified NOFOs and talk about overall approach. + * Discuss vision, goals, and process for hackathon. + * Press invited to event + * Regular meetings scheduled for duration of hackathon (e.g., once every two weeks for demos and questions) + * Closing ceremonies scheduled for 2-3 months later hackathon-listing-internal-entry: status: heading: "Hackathon NOFO listing: Internal entry" diagram_name: hackathon-listing-Internal-Entry dependencies: [hackathon-listing, research-synthesis] - description: TODO add description + description: | + A submission, by the software development modernization team for grants.gov, for the hackathon phase 1: listing page. hackathon-apply: status: heading: "Hackathon Phase 2: Apply" diagram_name: Hackathon-Apply dependencies: [hackathon-listing] - description: TODO add description + description: | + Phase 2 of the hackathon: the application. hackathon-apply-internal-entry: status: heading: "Hackathon NOFO Apply: Internal entry" diagram_name: Hackathon-Apply-Internal-Entry dependencies: [hackathon-apply, research-synthesis] - description: TODO add description + description: | + A submission, by the software development modernization team for grants.gov, for the hackathon phase 2: application page. hackathon-data: status: heading: "Hackathon Phase 3: Data" diagram_name: Hackathon-Data dependencies: [hackathon-apply, analytics-api] - description: TODO add description + description: | + Phase 3 of the hackathon: the data. hackathon-data-internal-entry: status: heading: "Hackathon data: Internal entry" diagram_name: Hackathon-Data-Internal-Entry dependencies: [hackathon-data] - description: TODO add description + description: | + A submission, by the software development modernization team for grants.gov, for the hackathon phase 2: data. user-interface: heading: User interface milestones description: Description of Auth milestones @@ -443,55 +694,93 @@ sections: heading: Front-end planning diagram_name: FE-Plan dependencies: null - description: TODO add description + description: | + Choose language (e.g., TypeScript), framework (e.g., React) and testing framework (e.g., Jest) for front-end. fe-ci-cd: status: heading: Front-end CI-CD diagram_name: FE-CI-CD dependencies: [fe-plan] - description: TODO add description + description: | + Install developer tools for front-end, including: + + - Automated test framework + - Linters, code quality checkers, and code autoformatters + - Automated test coverage analysis + - Security scan of packages (Snyk or alternative) + - Automated accessibility testing + - Mobile responsiveness testing + - Funding needs of packages (https://backyourstack.com/ or alternative) + - etc + + These should all run on CI-CD like the definition of the milestone `CI-CD`. cms: status: heading: CMS diagram_name: CMS dependencies: [fe-plan] - description: TODO add description + description: | + Choose and implement a CMS, such as the headless CMS Storyblok being implemented by grants.gov. cms-open-source: status: heading: Open source CMS content diagram_name: CMS-Open-Source dependencies: [cms] - description: TODO add description + description: | + Implement CMS in such a way so that the content is tracked and version controlled in an open source fashion, if this is at all possible. web-analytics: status: heading: Web analytics diagram_name: Web-Analytics dependencies: [fe-plan] - description: TODO add description + description: | + Choose and implement a web analytics framework, such as analytics.gov or Google Analytics, where all data can be made publicly available. web-analytics-legacy: status: heading: Grants.gov analytics diagram_name: Web-Analytics-Legacy dependencies: null - description: TODO add description + description: | + Establish existing baseline of customer satisfaction and other analytics in existing grants.gov. + + The longer we have this baseline data, the more we can use the baseline comparison to ensure that beta.grants.gov provides an improved user experience with higher rates of customer satisfaction. i18n: status: heading: Internationalization framework diagram_name: i18n dependencies: [fe-plan, cms] - description: TODO add description + description: | + Implement an internationalization framework (to support multiple languages) we believe can scale as the site scales. + + Invest early in this. It is much easier to build internationalization in from the beginning than it is to add support for it to an existing codebase. translation-process: status: northStar heading: Translation process & contracts diagram_name: Translation-Process dependencies: [i18n, cms-open-source] - description: TODO add description + description: | + Implement a process (preferably an automated one) that makes translations quickly and accurately available for content. + + This could include using the State Department for translation like many USDS websites do, or setting up external bounties for translation such as those used by Open Collective Foundation: https://docs.opencollective.com/help/contributing/translation#bounties. + + Preference for integrating the external team that does translations directly into the codebase so they can make their own pull requests to content. + + Define languages planned on support for website. The most common languages are published by the Census: https://www.census.gov/library/stories/2022/12/languages-we-speak-in-united-states.html. A reasonable option for a starting point for supported languages would be Spanish and either Mandarin or Cantonese (the Census marks both of these as 'Chinese' so it is not clear which is the 3rd most commonly spoken language in the US). + + For language data, there is more detailed microdata found in the Microdata Access Tool (MDAT). Here's a table using the 2021 1-year ACS Public Use Microdata Sample (PUMS) file: https://data.census.gov/mdat/#/search?ds=ACSPUMS1Y2021&rv=HHLANP&wt=WGTP. + + Ideally, every release of new content should be followed shortly thereafter by translations in key supported languages. Milestones will not be complete until their related content is released in all supported languages. foundational-ui: status: northStar heading: Foundational UI diagram_name: Foundational-UI dependencies: [fe-ci-cd, research-synthesis] - description: TODO add description + description: | + Build and deploy a basic UI framework for the site, using USWDS, informed by research synthesis. + + This includes basic information architecture, navigation, path parameters for navigating to different pages, the capability to complete query parameters, etc. + + Responsive design should be included from the beginning and integrated into testing. search: heading: Search milestones description: Description of Auth milestones @@ -502,49 +791,63 @@ sections: heading: Search API diagram_name: Search-API dependencies: [get-opportunities] - description: TODO add description + description: | + 1.0 version of an API to provide better searching on opportunities. search-ui: status: northStar heading: Search UI diagram_name: Search-UI dependencies: [search-api, foundational-ui] - description: TODO add description + description: | + Target date: aim to launch this in beta.grants.gov by sometime between December 2024 and March 2024. + + Front-end for search API. nofos-text: status: heading: Full text of NOFOs stored as text diagram_name: NOFOs-Text dependencies: [get-opportunities] - description: TODO add description + description: | + Convert all PDFs of NOFO listings (using their XML-based representation) into free text that can be stored in the database(s) and searched. nofos-text-search: status: heading: Full text search of NOFOs diagram_name: NOFOs-Text-Search dependencies: [nofos-text, search-api] - description: TODO add description + description: | + Search free text of NOFOs. search-api-2.0: status: heading: Tuned search API (Search 2.0) diagram_name: Search-API-2.0 dependencies: [nofos-text-search] - description: TODO add description + description: | + Tune the search API endpoint to give better results based on what users are looking for. geographic-search: status: heading: Geographic search diagram_name: Geographic-Search dependencies: [search-api] - description: TODO add description + description: | + Organizations are located in and/or serving very specific geographies. Currently, there's no way to add geographic search filters to results. For instance, searching "environmental justice" returns a lot of results for international aid or for specific countries (e.g., Mexico) that will be irrelevant to applicants from Alabama. + + Build a search API feature that, for at least some NOFOs, restricts search results to the geographies that are affiliated with the applicant that is searching for NOFOs. + + The challenge with this milestone will be that few NOFOs have easily accessible, clearly tagged geographic data. Perhaps we can setup a system of adding this data per NOFO, or crowdsourcing the data with verification. search-user-research: status: heading: User research for search diagram_name: Search-User-Research dependencies: [search-api] - description: TODO add description + description: | + Conduct additional user research on search needs. additional-search: status: heading: Additional search diagram_name: Additional-Search dependencies: [search-user-research] - description: TODO add description + description: | + Add additional search (e.g., by community type, by capital availability, etc) features driven by user research's identification of search needs. personalization: heading: Personalization milestones description: Description of Auth milestones @@ -555,31 +858,38 @@ sections: heading: Save NOFOs to profile diagram_name: Save-NOFOs dependencies: [entity-api] - description: TODO add description + description: | + Possibly allow users the ability to mark a NOFO to be "saved" to their profile, for instance as an opportunity they're interested in working on. nofos-worked: status: heading: Show NOFOs the user has worked on diagram_name: NOFOs-Worked dependencies: [entity-api] - description: TODO add description + description: | + Possibly allow users the ability to see which NOFOs they have submitted in the past or are currently working on. nofo-recommendations-api: status: northStar heading: NOFO Recommendations API diagram_name: NOFO-Recommendations-API dependencies: [save-nofos, nofos-worked, profile-builder] - description: TODO add description + description: | + Possibly implement API that generates recommendations for users to look at certain opportunities based on their profile, their "saved" NOFOs and the NOFOs they have previously submitted or worked on. nofo-recommendations-ui: status: heading: NOFO Recommendations UI diagram_name: NOFO-Recommendations-UI dependencies: [nofo-recommendations-api, search-ui] - description: TODO add description + description: | + UI for recommendations. profile-builder: status: heading: Profile builder diagram_name: Profile-Builder dependencies: [entity-api] - description: TODO add description + description: | + Possibly implement API that allows users to interact with their profile, and update characteristics such as their entity type (e.g., small local government, Tribal, CBO, etc), their capacity for matching funds (e.g., <=$20m), etc. + + Particular attention should be paid to profile fields that are relevant for finding related federal opportunities. user-research: heading: User research milestones description: Description of Auth milestones @@ -590,49 +900,95 @@ sections: heading: Generative user research diagram_name: Generative-User-Research dependencies: null - description: TODO add description + description: | + Since January 25, 2023, Huge has conducted in-depth one-on-one interviews with applicants and grantors to understand the end user experience of Grants.gov. With the aim of creating a more equitable Grants.gov, Huge explored key barriers for underserved and first-time applicants, and identified areas for improvement along the grantor and applicant user journeys. Focusing specifically on Find and Apply functions, Huge examined the role of Grants.gov in the broader grant opportunity ecosystem and examined specific pain points that hinder the grant seeking process for end users. + + The results of these findings will be incorporated in the Phase II Vision presentation, which will feature user archetypes and a strategic vision for a more equitable Grants.gov. The purpose of this document is to serve as a topline preview of grantor and applicant insights as the Huge team continues to synthesize research findings and inform the overall vision. + + Research Objectives + + 1. Exploration of the role of Grants.gov in the broader ecosystem: Examine existing user behaviors in the grant seeker's and grantor's journey, identifying where Grants.gov fits in their overall approach and what role it provides in relation to other systems. + 2. Evaluation of semi-structured Grants.gov experience: Investigate participants' experience, including their organic user journeys based on their role, features of Grants.gov that standout, and any challenges they have with the interaction along the way. + 3. Evaluation of specific task completion: Observe participants' pain points and delights while they attempt to complete specific journeys related to their roles in posting, finding, and applying to grants, and their ability to complete related tasks. + 4. Exploration of opportunities for an equitable experience: Identify key barriers to and areas of opportunity where grants.gov can help increase applications for underserved communities. + + Methodology + + * Method: Moderated in-depth interviews + * LOI: 60-75 minutes + * Platform: Google Meet + * Audience: + - n=12 Grantors + - n=38 Applicants (26% First Time Applicants, 39% Casual/Occasional Applicants, 34% Serial/Frequent Applicants) + * Field Dates - 1/25/2023 - 3/3/2023 research-synthesis: status: executing heading: Research synthesis and vision diagram_name: Research-synthesis dependencies: [generative-user-research] - description: TODO add description + description: | + Note: Research synthesis must inform all UI designs. This relationship is not always marked on the diagram due to the complexity of the arrows, but it is a necessary dependency for all UI designs. + + Deliverables from Huge: + + * Research synthesis - Huge will compile findings from the user research sessions to identify key insights. In doing this, we will highlight user pain points and challenges as well as features and functionalities that are liked by users today. + * Archetype definition - Based on research findings, Huge will define user archetypes structured by needs and behavioral attributes. These archetypes create a common, shared understanding of users’ behaviors, attitudes, goals, and pain points that will need to be met by the future state of Grants.gov. + * UX explorations - The Huge UX lead will design initial wireframes that will explore identified areas of opportunity as a way to highlight future state solutions for Grants.gov. Wireframes will be illustrative but will provide a solid foundation for planning and development of high fidelity design. + * Visual design explorations - The Huge team will ideate on how to improve the current Grants.gov design system visually. Explorations will be illustrative but could be iterated on in future phases of work. + * Vision creation - The vision for Grants.gov will summarize our recommended ‘North Star’ for the product ecosystem. This north star will provide the guardrails for planning the subsequent design and release phases. + * Roadmap planning - Based on the shared vision, Huge will define a sequenced roadmap for how to bring to life Grants.gov that takes into account regular, iterative releases of key functionality. document-grantsolutions: status: heading: Document GrantSolutions integration points diagram_name: Document-GrantSolutions dependencies: null - description: TODO add description + description: | + Document all tasks that need to be completed on GrantSolutions to enable functionality on grants.gov. For instance, to post a NOFO to grants.gov, you may (or may not) need to go through the announcement module of GrantSolutions. + + Document which of these tasks can be completed using APIs vs which must go through the GrantSolutions.gov user interface. user-research-compensation: status: heading: User research compensation diagram_name: User-Research-Compensation dependencies: null - description: TODO add description + description: | + Investigate potentially setting up a system for compensation user research participants, modeled after the compensation system developed by USDS at the White House. + + This will promote fairness, as well as increasing the quality and diversity of our pool of user research participants, by compensating participants for sharing their lived experience and expertise. user-research-database: status: heading: User research participants database diagram_name: User-Research-Database dependencies: null - description: TODO add description + description: | + Create a database that is easy to maintain and update of potential participants for ongoing user research to contact about future user research opportunities. + + Ideally, this database would also have some (appropriate) characteristics of the people stored so that when we're looking to talk to individuals representing certain characteristics, we can narrow our search to those people. user-research-tbd: status: heading: Additional user research TBD diagram_name: User-Research-TBD dependencies: [research-synthesis] - description: TODO add description + description: | + Additional user research milestones to be defined later on topics such as search, apply, etc. These need to be fleshed out in detail. user-research-nofo-writing: status: heading: NOFO writing journey mapping diagram_name: User-Research-NOFO-Writing dependencies: [research-synthesis] - description: TODO add description + description: | + Conduct user research on the process of grantors writing NOFO listings. + + This includes the stages of planning, budgeting, designing, developing, writing, getting approvals on content, and posting. + + Producing a user journey on this topic would be essential for developing features relating to enhanced NOFO listing pages, because we need to understand how users write and post their NOFOs. unified-brand: status: heading: Unified cross-platform branding and identity diagram_name: Unified-Brand dependencies: [research-synthesis] - description: TODO add description + description: | + Develop a unified brand and visual/comms identity that communicates Office of Grants and/or grants.gov vision and principles across multiple platforms (web, email, PowerPoint, etc). expanded-ui: heading: Expanded UI milestones description: Description of Auth milestones @@ -643,40 +999,65 @@ sections: heading: NOFO Listing page diagram_name: NOFO-Listing-UI dependencies: [foundational-ui, research-synthesis] - description: TODO add description + description: | + A page for individual NOFO listings. + + Should likely be built using configurable page template. For instance, using query parameters (or path parameters?) users should be able to view the NOFO listing in an alternative template format. This will make it easier to try new, modified designs for improved user experiences in the future and allow customization for users. (For instance, a third party could contribute a template via open source code that can be easily configured by users who want to use that third-party template.) + + This milestone uses *only* the standard fields and data that are available for all NOFOs in the grants.gov legacy database. nofo-listing-ui-enhanced: status: northStar heading: Enhanced NOFO Listing page diagram_name: NOFO-Listing-UI-Enhanced dependencies: [nofo-listing-ui, user-research-nofo-writing] - description: TODO add description + description: | + Target launch date: Between January 2024 and March 2024 + + An enhanced page for individual NOFO listings, to be launched with at least 5 real, live NOFOs by approximately March 2024. + + This listing page should have interactive contents, informed by best practices in design and user research on the topic, that make the content of the NOFOs easily accessible to users. The content should have a better user experience than simply a link to a PDF. + + In planning this milestone, it's useful to know that most HHS OpDivs develop and submit NOFOs through GrantSolutions, using templates within GrantSolutions. GrantSolutions also provides a structured review process that facilitates getting several rounds of review of the NOFO listing and approvals from various departments (legal, budget, etc) before any content is approved as final. When the final approvals are in and the user chooses to submit the NOFO listing, GrantSolutions then posts the content over system-to-system connections to grants.gov. + + One challenge of this milestone is that unlike its dependency, `NOFO-Listing-UI`, it may require using specialized content that is not available through the legacy grants.gov databases. This may require setting up a new service or API that allows this new content to be submitted. This may require updating GrantSolutions and its accompanying workflows to support that new content. + + Alternatively, the content management for new NOFO listings may be as simple as configuring a headless CMS to support permissioning and approvals by the program staff. More user research is necessary to understand the journey map of NOFO writers and ensure that this enhanced NOFO listing milestone has all of its dependencies known. + + Definition of done: + * For five or more NOFOs, program staff writing the NOFOs are able to submit the listing's content through an automated service of some kind that then populates the data onto beta.grants.gov. + * For five or more NOFOs, those listings are available in production at beta.grants.gov with an enhanced page listing experience for real programs. sam-assistance-ui: status: heading: Sam.gov assistance page diagram_name: Sam-Assistance-UI dependencies: [foundational-ui, document-sam, research-synthesis] - description: TODO add description + description: | + A page for helping people understand and navigate their necessary tasks on Sam.gov. grants-assistance-ui: status: heading: Grants.gov assistance page diagram_name: Grants-Assistance-UI dependencies: [foundational-ui, research-synthesis] - description: TODO add description + description: | + A page for helping people understand and navigate their necessary tasks on Grants.gov. help-ui: status: heading: Help page diagram_name: Help-UI dependencies: [foundational-ui, research-synthesis] - description: TODO add description + description: | + A page for helping people contact the help desk and other avenues for seeking customer support. authn-ui: status: heading: Authentication (authN) page diagram_name: AuthN-UI dependencies: [foundational-ui, authn-api, research-synthesis] - description: TODO add description + description: | + A workflow for helping people create accounts (via Login.gov) for use on Grants.gov. authz-ui: status: heading: Authorization (authZ) page diagram_name: AuthZ-UI dependencies: [foundational-ui, authz-api, research-synthesis] - description: TODO add description + description: | + A workflow for helping people manage roles for use on Grants.gov. From c926724a299b28f48adedfeb79d0051fa27b45fc Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 6 Jun 2023 14:28:06 -0400 Subject: [PATCH 18/22] build: Adds mypy to project dependencies --- milestone-cli/poetry.lock | 640 +++++++++++++++++++---------------- milestone-cli/pyproject.toml | 4 +- 2 files changed, 342 insertions(+), 302 deletions(-) diff --git a/milestone-cli/poetry.lock b/milestone-cli/poetry.lock index 280c64159..bfeca632a 100644 --- a/milestone-cli/poetry.lock +++ b/milestone-cli/poetry.lock @@ -1,10 +1,38 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + [[package]] name = "black" version = "23.3.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +] [package.dependencies] click = ">=8.0.0" @@ -24,25 +52,34 @@ uvloop = ["uvloop (>=0.15.2)"] name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, +] [[package]] name = "chardet" version = "5.1.0" description = "Universal encoding detector for Python 3" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, + {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, +] [[package]] name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -51,25 +88,34 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "distlib" version = "0.3.6" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] [[package]] name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] [package.extras] test = ["pytest (>=6)"] @@ -78,29 +124,38 @@ test = ["pytest (>=6)"] name = "filelock" version = "3.12.0" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] [package.extras] -docs = ["furo (>=2023.3.27)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx (>=6.1.3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)", "pytest (>=7.3.1)"] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] [[package]] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] [package.dependencies] MarkupSafe = ">=2.0" @@ -110,55 +165,167 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" -version = "2.1.2" +version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "mypy" +version = "1.3.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d"}, + {file = "mypy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85"}, + {file = "mypy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd"}, + {file = "mypy-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152"}, + {file = "mypy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228"}, + {file = "mypy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd"}, + {file = "mypy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c"}, + {file = "mypy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae"}, + {file = "mypy-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca"}, + {file = "mypy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf"}, + {file = "mypy-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409"}, + {file = "mypy-1.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929"}, + {file = "mypy-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a"}, + {file = "mypy-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee"}, + {file = "mypy-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f"}, + {file = "mypy-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb"}, + {file = "mypy-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4"}, + {file = "mypy-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305"}, + {file = "mypy-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf"}, + {file = "mypy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8"}, + {file = "mypy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703"}, + {file = "mypy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017"}, + {file = "mypy-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e"}, + {file = "mypy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a"}, + {file = "mypy-1.3.0-py3-none-any.whl", hash = "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897"}, + {file = "mypy-1.3.0.tar.gz", hash = "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] [[package]] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] [[package]] name = "pathspec" version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] [[package]] name = "platformdirs" version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx (>=6.2.1)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest (>=7.3.1)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.extras] dev = ["pre-commit", "tox"] @@ -168,9 +335,46 @@ testing = ["pytest", "pytest-benchmark"] name = "pydantic" version = "1.10.8" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, + {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, + {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, + {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, + {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, + {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, + {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, + {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, + {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, + {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, + {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, + {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, + {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, + {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, + {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, + {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, + {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, + {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, + {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, + {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, + {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, + {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, + {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, +] [package.dependencies] typing-extensions = ">=4.2.0" @@ -183,25 +387,31 @@ email = ["email-validator (>=1.0.3)"] name = "pyproject-api" version = "1.5.1" description = "API to interact with the python pyproject.toml based projects" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pyproject_api-1.5.1-py3-none-any.whl", hash = "sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"}, + {file = "pyproject_api-1.5.1.tar.gz", hash = "sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9"}, +] [package.dependencies] packaging = ">=23" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)", "sphinx (>=6.1.3)"] -testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest (>=7.2.1)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] [[package]] name = "pytest" version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} @@ -218,33 +428,98 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] [[package]] name = "ruff" version = "0.0.270" description = "An extremely fast Python linter, written in Rust." -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, + {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"}, + {file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"}, + {file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"}, + {file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"}, + {file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"}, +] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] [[package]] name = "tox" -version = "4.5.2" +version = "4.6.0" description = "tox is a generic virtualenv management and test command line tool" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "tox-4.6.0-py3-none-any.whl", hash = "sha256:4874000453e637a87ca892f9744a2ab9a7d24064dad1b0ecbf5a4c3c146cc732"}, + {file = "tox-4.6.0.tar.gz", hash = "sha256:954f1f647f67f481d239a193288983242a6152b67503c4a56b19a4aafaa29736"}, +] [package.dependencies] cachetools = ">=5.3" @@ -259,42 +534,62 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} virtualenv = ">=20.23" [package.extras] -docs = ["furo (>=2023.5.20)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinx (>=7.0.1)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17)", "psutil (>=5.9.5)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.3.1)", "pytest (>=7.3.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17)", "psutil (>=5.9.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] [[package]] name = "typer" version = "0.9.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] [package.dependencies] click = ">=7.1.1,<9.0.0" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)", "rich (>=10.11.0,<14.0.0)"] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "pillow (>=9.3.0,<10.0.0)", "cairosvg (>=2.5.2,<3.0.0)"] -test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "coverage (>=6.2,<7.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)", "rich (>=10.11.0,<14.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.10" +description = "Typing stubs for PyYAML" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-6.0.12.10.tar.gz", hash = "sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97"}, + {file = "types_PyYAML-6.0.12.10-py3-none-any.whl", hash = "sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f"}, +] [[package]] name = "typing-extensions" -version = "4.6.2" +version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, +] [[package]] name = "virtualenv" version = "20.23.0" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, +] [package.dependencies] distlib = ">=0.3.6,<1" @@ -302,267 +597,10 @@ filelock = ">=3.11,<4" platformdirs = ">=3.2,<4" [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx-argparse (>=0.4)", "sphinx (>=6.1.3)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.3)", "coverage-enable-subprocess (>=1)", "coverage (>=7.2.3)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "pytest (>=7.3.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] [metadata] -lock-version = "1.1" +lock-version = "2.0" python-versions = "^3.10" -content-hash = "2eeaf092810b25acff4ead4b806b3a0d81f1e88333cc4e43bf90fbd3239a65e5" - -[metadata.files] -black = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, -] -cachetools = [ - {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, - {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, -] -chardet = [ - {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, - {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -distlib = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] -exceptiongroup = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, -] -filelock = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, -] -iniconfig = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, -] -mypy-extensions = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] -packaging = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] -pathspec = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, -] -platformdirs = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pydantic = [ - {file = "pydantic-1.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d"}, - {file = "pydantic-1.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"}, - {file = "pydantic-1.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277"}, - {file = "pydantic-1.10.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab"}, - {file = "pydantic-1.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33"}, - {file = "pydantic-1.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85"}, - {file = "pydantic-1.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e"}, - {file = "pydantic-1.10.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4"}, - {file = "pydantic-1.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd"}, - {file = "pydantic-1.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4"}, - {file = "pydantic-1.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68"}, - {file = "pydantic-1.10.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea"}, - {file = "pydantic-1.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887"}, - {file = "pydantic-1.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18"}, - {file = "pydantic-1.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1"}, - {file = "pydantic-1.10.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108"}, - {file = "pydantic-1.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e"}, - {file = "pydantic-1.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459"}, - {file = "pydantic-1.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1"}, - {file = "pydantic-1.10.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01"}, - {file = "pydantic-1.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a"}, - {file = "pydantic-1.10.8-py3-none-any.whl", hash = "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2"}, - {file = "pydantic-1.10.8.tar.gz", hash = "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca"}, -] -pyproject-api = [ - {file = "pyproject_api-1.5.1-py3-none-any.whl", hash = "sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"}, - {file = "pyproject_api-1.5.1.tar.gz", hash = "sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9"}, -] -pytest = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -ruff = [ - {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, - {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"}, - {file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"}, - {file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"}, - {file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"}, - {file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -tox = [ - {file = "tox-4.5.2-py3-none-any.whl", hash = "sha256:f1a9541b292aa0449f6c7bb67dc0073f25f9086413c3922fe47f5168cbf7b2f4"}, - {file = "tox-4.5.2.tar.gz", hash = "sha256:ad87fb7a10ef476afb6eb7e408808057f42976ef0d30ad5fe023099ba493ce58"}, -] -typer = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, -] -typing-extensions = [ - {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, - {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, -] -virtualenv = [ - {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, - {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, -] +content-hash = "977329869e73d21e90a6a5a268bdbe82497e4d293e574a9236d44ad9e57e5ecd" diff --git a/milestone-cli/pyproject.toml b/milestone-cli/pyproject.toml index 09b1da15e..335732199 100644 --- a/milestone-cli/pyproject.toml +++ b/milestone-cli/pyproject.toml @@ -7,15 +7,17 @@ version = "0.1.0" [tool.poetry.dependencies] Jinja2 = "^3.1.2" PyYAML = "^6.0" +mypy = "^1.3.0" pydantic = "^1.10.8" python = "^3.10" typer = "^0.9.0" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] black = "^23.3.0" pytest = "^7.3.1" ruff = "^0.0.270" tox = "^4.5.2" +types-pyyaml = "^6.0.12.10" [build-system] build-backend = "poetry.core.masonry.api" From 75ace401b694e352beb33d7c6124f5192b222b93 Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 6 Jun 2023 14:28:48 -0400 Subject: [PATCH 19/22] fix: Type errors raised by mypy --- milestone-cli/milestone_cli/cli.py | 8 +++++--- milestone-cli/milestone_cli/utils.py | 2 +- milestone-cli/tests/test_schemas.py | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/milestone-cli/milestone_cli/cli.py b/milestone-cli/milestone_cli/cli.py index 7bca54671..a81ceaef0 100644 --- a/milestone-cli/milestone_cli/cli.py +++ b/milestone-cli/milestone_cli/cli.py @@ -35,7 +35,7 @@ def hello_world(name: str | None = None): @app.command(name="validate") def validate_yaml_file_contents( yaml_file: str | None = None, -) -> MilestoneSummary: +) -> MilestoneSummary | None: """Loads MilestoneSummary from yaml file and validates contents""" if not yaml_file: file_path = MILESTONE_FILE @@ -43,10 +43,10 @@ def validate_yaml_file_contents( file_path = Path(yaml_file).absolute() if not file_path.exists(): print(f"No file found at {file_path}") - return + return None if file_path.suffix not in (".yaml", ".yml"): print(f"{file_path} is not a path to a yaml file") - return + return None milestones = load_milestones_from_yaml_file(file_path) print("Everything looks good") return milestones @@ -67,6 +67,8 @@ def populate_output_file( print("Command must either be 'populate summary' or 'populate diagram'") # load milestones and get output path milestones = validate_yaml_file_contents(yaml_file) + if not milestones: + return file_path = Path(output_file).absolute() print(f"Writing {kind} to {file_path}") # create or replace output file with rendered template diff --git a/milestone-cli/milestone_cli/utils.py b/milestone-cli/milestone_cli/utils.py index db3c649b4..f786c462c 100644 --- a/milestone-cli/milestone_cli/utils.py +++ b/milestone-cli/milestone_cli/utils.py @@ -13,7 +13,7 @@ def load_milestones_from_yaml_file(file_path: Path) -> MilestoneSummary: return MilestoneSummary(**contents) -def render_milestone_template(template_path: Path, params: str) -> str: +def render_milestone_template(template_path: Path, params: dict) -> str: """Load and populate a milestone template with the parameters provided""" template = jinja2.Template(template_path.read_text()) return template.render(params=params) diff --git a/milestone-cli/tests/test_schemas.py b/milestone-cli/tests/test_schemas.py index bc06aff9a..61239cd1a 100644 --- a/milestone-cli/tests/test_schemas.py +++ b/milestone-cli/tests/test_schemas.py @@ -72,9 +72,9 @@ def test_returns_correct_dependencies( ) -> None: """Should return a separate Dependency object for each dependency""" # setup - goals = summary.milestones.get("define-goals") - measure = summary.milestones.get("measurement-strategy") - comms = summary.milestones.get("comms-platform") + goals = summary.milestones["define-goals"] + measure = summary.milestones["measurement-strategy"] + comms = summary.milestones["comms-platform"] expected = [ Dependency(upstream=goals, downstream=measure), Dependency(upstream=goals, downstream=comms), @@ -118,7 +118,7 @@ def test_includes_dependencies_for_diagram( ) -> None: """""" # setup - dependencies = params.get("dependencies") + dependencies: list = params["dependencies"] expected = { "upstream": "Define-Goals", "downstream": "Measurement-Strategy", From 2ec6b0413c59d917cd7c79d20a26029919750a7b Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 6 Jun 2023 14:32:50 -0400 Subject: [PATCH 20/22] feat: Adds Makefile to simplify setup and dev commands --- milestone-cli/Makefile | 35 ++++++++++++++++++++++++++++++++++ milestone-cli/requirements.txt | 29 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 milestone-cli/Makefile create mode 100644 milestone-cli/requirements.txt diff --git a/milestone-cli/Makefile b/milestone-cli/Makefile new file mode 100644 index 000000000..318be2f28 --- /dev/null +++ b/milestone-cli/Makefile @@ -0,0 +1,35 @@ +diagram_path := ./diagram.mmd +summary_path := ./summary.md + +install: + poetry install + +lint: + poetry run black milestone_cli tests + poetry run ruff milestone_cli tests --fix + poetry run mypy milestone_cli tests + +requirements: + poetry export -f requirements.txt -o requirements.txt --with dev --without-hashes + echo ". # installs project" >> requirements.txt + +tox: + make requirements + poetry run tox + +milestones-check: + poetry run milestones validate + +milestones-diagram: +ifneq ($(output_path),) + poetry run milestones populate diagram $(output_path) +else + poetry run milestones populate diagram $(diagram_path) +endif + +milestones-summary: +ifneq ($(output_path),) + poetry run milestones populate summary $(output_path) +else + poetry run milestones populate summary $(summary_path) +endif diff --git a/milestone-cli/requirements.txt b/milestone-cli/requirements.txt new file mode 100644 index 000000000..30d0ac329 --- /dev/null +++ b/milestone-cli/requirements.txt @@ -0,0 +1,29 @@ +black==23.3.0 ; python_version >= "3.10" and python_version < "4.0" +cachetools==5.3.1 ; python_version >= "3.10" and python_version < "4.0" +chardet==5.1.0 ; python_version >= "3.10" and python_version < "4.0" +click==8.1.3 ; python_version >= "3.10" and python_version < "4.0" +colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" +distlib==0.3.6 ; python_version >= "3.10" and python_version < "4.0" +exceptiongroup==1.1.1 ; python_version >= "3.10" and python_version < "3.11" +filelock==3.12.0 ; python_version >= "3.10" and python_version < "4.0" +iniconfig==2.0.0 ; python_version >= "3.10" and python_version < "4.0" +jinja2==3.1.2 ; python_version >= "3.10" and python_version < "4.0" +markupsafe==2.1.3 ; python_version >= "3.10" and python_version < "4.0" +mypy-extensions==1.0.0 ; python_version >= "3.10" and python_version < "4.0" +mypy==1.3.0 ; python_version >= "3.10" and python_version < "4.0" +packaging==23.1 ; python_version >= "3.10" and python_version < "4.0" +pathspec==0.11.1 ; python_version >= "3.10" and python_version < "4.0" +platformdirs==3.5.1 ; python_version >= "3.10" and python_version < "4.0" +pluggy==1.0.0 ; python_version >= "3.10" and python_version < "4.0" +pydantic==1.10.8 ; python_version >= "3.10" and python_version < "4.0" +pyproject-api==1.5.1 ; python_version >= "3.10" and python_version < "4.0" +pytest==7.3.1 ; python_version >= "3.10" and python_version < "4.0" +pyyaml==6.0 ; python_version >= "3.10" and python_version < "4.0" +ruff==0.0.270 ; python_version >= "3.10" and python_version < "4.0" +tomli==2.0.1 ; python_version >= "3.10" and python_version < "3.11" +tox==4.6.0 ; python_version >= "3.10" and python_version < "4.0" +typer==0.9.0 ; python_version >= "3.10" and python_version < "4.0" +types-pyyaml==6.0.12.10 ; python_version >= "3.10" and python_version < "4.0" +typing-extensions==4.6.3 ; python_version >= "3.10" and python_version < "4.0" +virtualenv==20.23.0 ; python_version >= "3.10" and python_version < "4.0" +. # installs project From 9e8a2cacee43cfe8467b1b2cfbfd54d95465bd71 Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 6 Jun 2023 14:42:18 -0400 Subject: [PATCH 21/22] feat: Add tox file to orchestrate checks --- milestone-cli/tox.ini | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 milestone-cli/tox.ini diff --git a/milestone-cli/tox.ini b/milestone-cli/tox.ini new file mode 100644 index 000000000..9a5cbd8b8 --- /dev/null +++ b/milestone-cli/tox.ini @@ -0,0 +1,37 @@ +[tox] +# tests 3.19 and runs each of the testenvs below +envlist = + py310 + lint + typecheck + pytest +skip_missing_interpreters = true +isolated_build = true + +[testenv] +deps = + Jinja2 + PyYAML + pydantic + typer + pytest + +[testenv:lint] +# lints python code in milestone_cli and tests +basepython = python3.10 +deps = + black + ruff +commands = + black milestone_cli tests + ruff milestone_cli tests + +[testenv:typecheck] +deps = + mypy + types-pyyaml +commands = mypy milestone_cli + +[testenv:pytest] +deps = pytest +commands = pytest tests From e2280e51f1250c15793c2077e08431e965bafbc1 Mon Sep 17 00:00:00 2001 From: widal001 Date: Tue, 6 Jun 2023 23:15:59 -0400 Subject: [PATCH 22/22] docs: Improves README and docstrings --- milestone-cli/README.md | 12 +++--- milestone-cli/milestone_cli/cli.py | 19 ++++++++- milestone-cli/milestone_cli/schemas.py | 57 ++++++++++++++++++++++---- milestone-cli/milestone_cli/utils.py | 20 ++++++++- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/milestone-cli/README.md b/milestone-cli/README.md index ef6e6698a..4539797fd 100644 --- a/milestone-cli/README.md +++ b/milestone-cli/README.md @@ -19,13 +19,11 @@ poetry --version ### Quickstart -1. Install the package: `poetry install` -2. Activate a shell: `poetry shell` -3. Validate the milestone yaml file: `milestones validate` -4. Populate the files: - - Diagram: `milestones populate diagram ./diagram.mmd` - - Summary: `milestones populate summary ./summary.md` -5. Exit the shell session: `exit` +1. Install the package: `make install` +2. Validate the milestone yaml file: `make milestones_check` +3. Populate the files: + - Diagram: `make milestones_diagram` + - Summary: `make milestones_summary` ## Made with diff --git a/milestone-cli/milestone_cli/cli.py b/milestone-cli/milestone_cli/cli.py index a81ceaef0..5a53e40f5 100644 --- a/milestone-cli/milestone_cli/cli.py +++ b/milestone-cli/milestone_cli/cli.py @@ -1,3 +1,4 @@ +"""Defines the command line entry points to load and populate milestone files""" from pathlib import Path import typer @@ -36,7 +37,15 @@ def hello_world(name: str | None = None): def validate_yaml_file_contents( yaml_file: str | None = None, ) -> MilestoneSummary | None: - """Loads MilestoneSummary from yaml file and validates contents""" + """Loads MilestoneSummary from yaml file and validates contents + + Args: + yaml_file: Pathlike string to the yaml file which contains milestone details + + Returns: + An instance of MilestoneSummary when the details can be parsed from the + yaml file, None if the file doesn't exist or there are parsing errors + """ if not yaml_file: file_path = MILESTONE_FILE else: @@ -58,7 +67,13 @@ def populate_output_file( output_file: str, yaml_file: str | None = None, ) -> None: - """Populate either the diagram or the summary file""" + """Populate either the diagram or the summary file + + Args: + kind: The type of document we are populating, must be "diagram" or "summary" + output_file: Pathlike string to the output file to populate + yaml_file: Pathlike string to yaml file from which to load milestone details + """ if kind == "diagram": template_path = DIAGRAM_TEMPLATE elif kind == "summary": diff --git a/milestone-cli/milestone_cli/schemas.py b/milestone-cli/milestone_cli/schemas.py index 4d38df0ca..3d2888487 100644 --- a/milestone-cli/milestone_cli/schemas.py +++ b/milestone-cli/milestone_cli/schemas.py @@ -1,35 +1,61 @@ +"""Defines the data interfaces for milestone details""" from pydantic import BaseModel class MilestoneBase(BaseModel): - """Contains fields shared by Milestone and Section""" + """Contains fields shared by Milestone and Section + + Attributes: + heading: The heading as it will appear in the summary markdown doc + description: The description as it will appear in the summary markdown doc + diagram_name: The name as it will appear in the mermaid diagram + """ heading: str - diagram_name: str description: str + diagram_name: str class Milestone(MilestoneBase): - """Contains a summary of details about a project milestone""" + """Contains a summary of details about a project milestone + + Attributes: + status: The status of the milestone, which appears in the summary doc and + determines the styling applied to the node in the mermaid diagram + dependencies: A list of the upstream dependencies for this milestone + """ status: str | None = None dependencies: list[str] | None = None class MilestoneSection(MilestoneBase): - """Represents a group of project milestones""" + """Represents a group of project milestones + + Attributes: + milestones: The list of milestones that fall under this section + """ milestones: dict[str, Milestone] class Dependency(BaseModel): - """Documents a (downstream) milestone's dependency on an upstream milestone""" + """Documents a (downstream) milestone's dependency on an upstream milestone + + Attributes: + upstream: The milestone that the downstream milestone depends on + downstream: The downstream milestone that is blocked by the upstream milestone + """ upstream: Milestone downstream: Milestone def jinja_export(self) -> dict: - """Export just the diagram names for jinja templating""" + """Export just the diagram names for jinja templating + + Returns: + A dictionary of the diagram names for upstream and downstream dependencies + """ return { "upstream": self.upstream.diagram_name, "downstream": self.downstream.diagram_name, @@ -37,13 +63,26 @@ def jinja_export(self) -> dict: class MilestoneSummary(BaseModel): - """Stores the list of milestones and the sections they belong to""" + """Stores the list of milestones and the sections they belong to + + Attributes: + version: Version of the milestone summary pulled from the yaml file + sections: A dictionary that maps MilestoneSections to their keys + """ version: str sections: dict[str, MilestoneSection] def map_dependencies(self, milestone: Milestone) -> list[Dependency] | None: - """Split milestone dependencies into a list of Dependency objects""" + """Split milestone dependencies into a list of Dependency objects + + Args: + milestone: A Milestone object to map dependencies for + + Returns: + A list of Dependency objects for the given Milestone if it has + upstream depdencies, None otherwise + """ if not milestone.dependencies: return None deps = [] @@ -67,7 +106,7 @@ def dependencies(self) -> list[Dependency]: @property def milestones(self) -> dict[str, Milestone]: - """Returns a combined list of Milestone objects""" + """Returns a combined dictionary of Milestone objects mapped to their key""" return { milestone: details for section in self.sections.values() diff --git a/milestone-cli/milestone_cli/utils.py b/milestone-cli/milestone_cli/utils.py index f786c462c..6a0290432 100644 --- a/milestone-cli/milestone_cli/utils.py +++ b/milestone-cli/milestone_cli/utils.py @@ -1,3 +1,4 @@ +"""Defines the utility functions for loading milestones and populating templates""" from pathlib import Path import jinja2 @@ -7,14 +8,29 @@ def load_milestones_from_yaml_file(file_path: Path) -> MilestoneSummary: - """Load a yaml file and parse the contents into a MilestoneSummary object""" + """Load a yaml file and parse the contents into a MilestoneSummary object + + Args: + file_path: Path object to yaml file that contains milestone details + + Returns: + A MilestoneSummary instance which contains the details loaded from the yaml file + """ with open(file_path) as f: contents = yaml.safe_load(f) return MilestoneSummary(**contents) def render_milestone_template(template_path: Path, params: dict) -> str: - """Load and populate a milestone template with the parameters provided""" + """Load and populate a milestone template with the parameters provided + + Args: + template_path: Path object to the Jinja2 template document to render + params: A dictionary of params used to populate the Jinja2 template + + Returns: + A string of the rendered template populated with the params passed + """ template = jinja2.Template(template_path.read_text()) return template.render(params=params)