diff --git a/Makefile b/Makefile index 4ee41ee..2eb9197 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,16 @@ all: black test typehint lint black: - isort --multi-line 3 --profile black stacosys/ - black stacosys/ + poetry run isort --multi-line 3 --profile black stacosys/ + poetry run black stacosys/ -test: - pytest +test: + poetry run coverage run -m --source=stacosys pytest + poetry run coverage report typehint: - mypy --ignore-missing-imports stacosys/ + poetry run mypy --ignore-missing-imports stacosys/ lint: - pylint stacosys/ + poetry run pylint stacosys/ diff --git a/config.ini b/config.ini index 17e4cd3..5cd951a 100755 --- a/config.ini +++ b/config.ini @@ -6,7 +6,8 @@ db_sqlite_file = db.sqlite [site] name = "My blog" -url = http://blog.mydomain.com +proto = https +url = https://blog.mydomain.com admin_email = admin@mydomain.com redirect = /redirect @@ -15,7 +16,6 @@ host = 127.0.0.1 port = 8100 [rss] -proto = https file = comments.xml [smtp] diff --git a/poetry.lock b/poetry.lock index 2842c27..c5a9026 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,33 +1,6 @@ -[[package]] -name = "apscheduler" -version = "3.9.1.post1" -description = "In-process task scheduler with Cron-like capabilities" -category = "main" -optional = false -python-versions = "!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.dependencies] -pytz = "*" -setuptools = ">=0.7" -six = ">=1.4.0" -tzlocal = ">=2.0,<3.0.0 || >=4.0.0" - -[package.extras] -asyncio = ["trollius"] -doc = ["sphinx", "sphinx-rtd-theme"] -gevent = ["gevent"] -mongodb = ["pymongo (>=3.0)"] -redis = ["redis (>=3.0)"] -rethinkdb = ["rethinkdb (>=2.4.0)"] -sqlalchemy = ["sqlalchemy (>=0.8)"] -testing = ["mock", "pytest", "pytest-asyncio", "pytest-asyncio (<0.6)", "pytest-cov", "pytest-tornado5"] -tornado = ["tornado (>=4.3)"] -twisted = ["twisted"] -zookeeper = ["kazoo"] - [[package]] name = "astroid" -version = "2.12.12" +version = "2.12.13" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -119,17 +92,14 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 [[package]] name = "coverage" -version = "5.5" +version = "6.5.0" description = "Code coverage measurement for Python" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.dependencies] -toml = {version = "*", optional = true, markers = "extra == \"toml\""} +python-versions = ">=3.7" [package.extras] -toml = ["toml"] +toml = ["tomli"] [[package]] name = "coveralls" @@ -158,14 +128,6 @@ python-versions = ">=3.7" [package.extras] graph = ["objgraph (>=1.7.2)"] -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "docopt" version = "0.6.2" @@ -174,34 +136,22 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "filelock" -version = "3.8.0" -description = "A platform independent file lock." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] - [[package]] name = "flake8" -version = "5.0.4" +version = "6.0.0" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8.1" [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" +pycodestyle = ">=2.10.0,<2.11.0" +pyflakes = ">=3.0.0,<3.1.0" [[package]] name = "flake8-black" -version = "0.3.4" +version = "0.3.5" description = "flake8 plugin to call black as a code style validator" category = "dev" optional = false @@ -350,7 +300,7 @@ python-versions = "*" name = "packaging" version = "21.3" description = "Core utilities for Python packages" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -377,7 +327,7 @@ python-versions = "*" name = "platformdirs" version = "2.5.4" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" +category = "dev" optional = false python-versions = ">=3.7" @@ -389,7 +339,7 @@ test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -397,17 +347,9 @@ python-versions = ">=3.6" dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - [[package]] name = "pycodestyle" -version = "2.9.1" +version = "2.10.0" description = "Python style guide checker" category = "dev" optional = false @@ -415,7 +357,7 @@ python-versions = ">=3.6" [[package]] name = "pyflakes" -version = "2.5.0" +version = "3.0.1" description = "passive checker of Python programs" category = "dev" optional = false @@ -423,14 +365,14 @@ python-versions = ">=3.6" [[package]] name = "pylint" -version = "2.15.6" +version = "2.15.7" description = "python code static checker" category = "dev" optional = false python-versions = ">=3.7.2" [package.dependencies] -astroid = ">=2.12.12,<=2.14.0-dev0" +astroid = ">=2.12.13,<=2.14.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = ">=0.2" isort = ">=4.2.5,<6" @@ -446,7 +388,7 @@ testutils = ["gitpython (>3)"] name = "pyparsing" version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" +category = "dev" optional = false python-versions = ">=3.6.8" @@ -494,25 +436,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] -[[package]] -name = "pytz" -version = "2022.6" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pytz-deprecation-shim" -version = "0.1.0.post0" -description = "Shims to make deprecation of pytz easier" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - [[package]] name = "requests" version = "2.28.1" @@ -531,35 +454,6 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "setuptools" -version = "65.6.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - [[package]] name = "tomli" version = "2.0.1" @@ -576,27 +470,6 @@ category = "dev" optional = false python-versions = ">=3.6" -[[package]] -name = "tox" -version = "3.27.1" -description = "tox is a generic virtualenv management and test command line tool" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} -filelock = ">=3.0.0" -packaging = ">=14" -pluggy = ">=0.12.0" -py = ">=1.4.17" -six = ">=1.14.0" -virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" - -[package.extras] -docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] -testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] - [[package]] name = "types-markdown" version = "3.4.2.1" @@ -613,60 +486,19 @@ category = "dev" optional = false python-versions = ">=3.7" -[[package]] -name = "tzdata" -version = "2022.6" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" - -[[package]] -name = "tzlocal" -version = "4.2" -description = "tzinfo object for the local timezone" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pytz-deprecation-shim = "*" -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] -test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] - [[package]] name = "urllib3" -version = "1.26.12" +version = "1.26.13" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "virtualenv" -version = "20.16.7" -description = "Virtual Python Environment builder" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<3" - -[package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] - [[package]] name = "werkzeug" version = "2.2.2" @@ -692,16 +524,12 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [metadata] lock-version = "1.1" python-versions = "~3.11" -content-hash = "94614b000f5848489ed0e32caf25a06df9daa05bc063d0e2fe03a00f6859d27b" +content-hash = "c7fd5e51d22b64ab20394250b12819609483e7e825996bb8c62e6bb2bb537951" [metadata.files] -apscheduler = [ - {file = "APScheduler-3.9.1.post1-py2.py3-none-any.whl", hash = "sha256:c8c618241dbb2785ed5a687504b14cb1851d6f7b5a4edf3a51e39cc6a069967a"}, - {file = "APScheduler-3.9.1.post1.tar.gz", hash = "sha256:b2bea0309569da53a7261bfa0ce19c67ddbfe151bda776a6a907579fdbd3eb2a"}, -] astroid = [ - {file = "astroid-2.12.12-py3-none-any.whl", hash = "sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f"}, - {file = "astroid-2.12.12.tar.gz", hash = "sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83"}, + {file = "astroid-2.12.13-py3-none-any.whl", hash = "sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907"}, + {file = "astroid-2.12.13.tar.gz", hash = "sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"}, ] attrs = [ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, @@ -751,58 +579,56 @@ colorama = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] coverage = [ - {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] coveralls = [ {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, @@ -812,24 +638,16 @@ dill = [ {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, ] -distlib = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] -filelock = [ - {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, - {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, -] flake8 = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, + {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, + {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, ] flake8-black = [ - {file = "flake8-black-0.3.4.tar.gz", hash = "sha256:7f96a4c80a828d09f1d550724e16aabb2adacd6a5f8e0bb051df422fc63d2183"}, - {file = "flake8_black-0.3.4-py3-none-any.whl", hash = "sha256:fb52f258dfa6a25645c4ba8730eadc5f2ecd32057bf6c9fc21aef1cba9fefd74"}, + {file = "flake8-black-0.3.5.tar.gz", hash = "sha256:9e93252b1314a8eb3c2f55dec54a07239e502b12f57567f2c105f2202714b15e"}, + {file = "flake8_black-0.3.5-py3-none-any.whl", hash = "sha256:4948a579fdddd98fbf935fd94255dfcfce560c4ddc1ceee08e3f12d6114c8619"}, ] flask = [ {file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, @@ -981,21 +799,17 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] pycodestyle = [ - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, + {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, + {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, ] pyflakes = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, + {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, + {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, ] pylint = [ - {file = "pylint-2.15.6-py3-none-any.whl", hash = "sha256:15060cc22ed6830a4049cf40bc24977744df2e554d38da1b2657591de5bcd052"}, - {file = "pylint-2.15.6.tar.gz", hash = "sha256:25b13ddcf5af7d112cf96935e21806c1da60e676f952efb650130f2a4483421c"}, + {file = "pylint-2.15.7-py3-none-any.whl", hash = "sha256:1d561d1d3e8be9dd880edc685162fbdaa0409c88b9b7400873c0cf345602e326"}, + {file = "pylint-2.15.7.tar.gz", hash = "sha256:91e4776dbcb4b4d921a3e4b6fec669551107ba11f29d9199154a01622e460a57"}, ] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, @@ -1012,30 +826,10 @@ pytest-cov = [ {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, ] -pytz = [ - {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, - {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, -] -pytz-deprecation-shim = [ - {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, - {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, -] requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] -setuptools = [ - {file = "setuptools-65.6.0-py3-none-any.whl", hash = "sha256:6211d2f5eddad8757bd0484923ca7c0a6302ebc4ab32ea5e94357176e0ca0840"}, - {file = "setuptools-65.6.0.tar.gz", hash = "sha256:d1eebf881c6114e51df1664bc2c9133d022f78d12d5f4f665b9191f084e2862d"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -1044,10 +838,6 @@ tomlkit = [ {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, ] -tox = [ - {file = "tox-3.27.1-py2.py3-none-any.whl", hash = "sha256:f52ca66eae115fcfef0e77ef81fd107133d295c97c52df337adedb8dfac6ab84"}, - {file = "tox-3.27.1.tar.gz", hash = "sha256:b2a920e35a668cc06942ffd1cf3a4fb221a4d909ca72191fb6d84b0b18a7be04"}, -] types-markdown = [ {file = "types-Markdown-3.4.2.1.tar.gz", hash = "sha256:03c0904cf5886a7d8193e2f50bcf842afc89e0ab80f060f389f6c2635c65628f"}, {file = "types_Markdown-3.4.2.1-py3-none-any.whl", hash = "sha256:b2333f6f4b8f69af83de359e10a097e4a3f14bbd6d2484e1829d9b0ec56fa0cb"}, @@ -1056,21 +846,9 @@ typing-extensions = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] -tzdata = [ - {file = "tzdata-2022.6-py2.py3-none-any.whl", hash = "sha256:04a680bdc5b15750c39c12a448885a51134a27ec9af83667663f0b3a1bf3f342"}, - {file = "tzdata-2022.6.tar.gz", hash = "sha256:91f11db4503385928c15598c98573e3af07e7229181bee5375bd30f1695ddcae"}, -] -tzlocal = [ - {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, - {file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"}, -] urllib3 = [ - {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, - {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, -] -virtualenv = [ - {file = "virtualenv-20.16.7-py3-none-any.whl", hash = "sha256:efd66b00386fdb7dbe4822d172303f40cd05e50e01740b19ea42425cbe653e29"}, - {file = "virtualenv-20.16.7.tar.gz", hash = "sha256:8691e3ff9387f743e00f6bb20f70121f5e4f596cae754531f2b3b3a1b1ac696e"}, + {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, + {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, ] werkzeug = [ {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, diff --git a/pyproject.toml b/pyproject.toml index d1ec3da..f048766 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,19 +8,17 @@ include = ["run.py"] [tool.poetry.dependencies] python = "~3.11" -apscheduler = "^3.6.3" pyrss2gen = "^1.1" markdown = "^3.1.1" requests = "^2.25.1" -coverage = "^5.5" +coverage = "^6.5" peewee = "^3.14.8" -tox = "^3.24.5" background = "^0.2.1" Flask = "^2.1.1" types-markdown = "^3.4.2.1" [tool.poetry.group.dev.dependencies] -pylint = "^2.15.5" +pylint = "^2.15" mypy = "^0.991" pytest = "^7.2.0" coveralls = "^3.3.1" diff --git a/stacosys.sublime-workspace b/stacosys.sublime-workspace index 21684d5..a2e5ee9 100644 --- a/stacosys.sublime-workspace +++ b/stacosys.sublime-workspace @@ -3,6 +3,46 @@ { "selected_items": [ + [ + "c", + "check" + ], + [ + "con", + "configure_destination" + ], + [ + "EXP", + "EXPECTED_HTTP_PORT" + ], + [ + "auth", + "auth_login" + ], + [ + "l", + "login" + ], + [ + "assertI", + "assertIsNot" + ], + [ + "Confi", + "ConfigParameter" + ], + [ + "S", + "SITE_URL" + ], + [ + "Config", + "ConfigParameter" + ], + [ + "s", + "SITE_REDIRECT" + ], [ "get", "get_int" @@ -12,346 +52,81 @@ "buffers": [ { - "file": "run.sh", - "settings": - { - "buffer_size": 40, - "encoding": "UTF-8", - "line_ending": "Unix" - } - }, - { - "file": "stacosys/run.py", - "settings": - { - "buffer_size": 3298, - "encoding": "UTF-8", - "line_ending": "Unix" - }, - "undo_stack": + "file": "pyproject.toml", + "redo_stack": [ [ - 34, - 1, - "insert", - { - "characters": "no" - }, - "AgAAAOcFAAAAAAAA6AUAAAAAAAAAAAAA6AUAAAAAAADpBQAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADnBQAAAAAAAOcFAAAAAAAAAAAAAAAA8L8" - ], - [ - 35, - 1, - "insert", - { - "characters": " " - }, - "AQAAAOkFAAAAAAAA6gUAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADpBQAAAAAAAOkFAAAAAAAAAAAAAAAA8L8" - ], - [ - 36, - 1, - "left_delete", - null, - "AQAAAOkFAAAAAAAA6QUAAAAAAAABAAAAIA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADqBQAAAAAAAOoFAAAAAAAAAAAAAAAA8L8" - ], - [ - 37, - 1, - "insert", - { - "characters": "r" - }, - "AQAAAOkFAAAAAAAA6gUAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADpBQAAAAAAAOkFAAAAAAAAAAAAAAAA8L8" - ], - [ - 38, - 1, - "insert", - { - "characters": " " - }, - "AQAAAOoFAAAAAAAA6wUAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADqBQAAAAAAAOoFAAAAAAAAAAAAAAAA8L8" - ], - [ - 39, - 1, - "paste", - null, - "AQAAAOsFAAAAAAAA9gUAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADrBQAAAAAAAOsFAAAAAAAAAAAAAAAA8L8" - ], - [ - 40, - 1, - "insert", - { - "characters": " or" - }, - "AwAAAPYFAAAAAAAA9wUAAAAAAAAAAAAA9wUAAAAAAAD4BQAAAAAAAAAAAAD4BQAAAAAAAPkFAAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAD2BQAAAAAAAPYFAAAAAAAAAAAAAAAA8L8" - ], - [ - 41, - 1, - "insert", - { - "characters": " " - }, - "AQAAAPkFAAAAAAAA+gUAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAD5BQAAAAAAAPkFAAAAAAAAAAAAAAAA8L8" - ], - [ - 46, - 1, - "left_delete", - null, - "AQAAAOkFAAAAAAAA6QUAAAAAAAABAAAAcg", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADqBQAAAAAAAOoFAAAAAAAAAAAAAAAA8L8" - ], - [ - 47, + 84, 1, "insert", { - "characters": "t" + "characters": ".1" }, - "AQAAAOkFAAAAAAAA6gUAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADpBQAAAAAAAOkFAAAAAAAAAAAAAAAA8L8" - ], - [ - 59, - 1, - "insert_completion", - { - "completion": "lsp_select_completion_item {\"session_name\":\"LSP-pyright\",\"item\":{\"label\":\"get_int\",\"data\":{\"workspacePath\":\"/home/yannic/work/stacosys\",\"position\":{\"character\":21,\"line\":91},\"symbolLabel\":\"get_int\",\"filePath\":\"/home/yannic/work/stacosys/stacosys/run.py\"},\"kind\":2,\"sortText\":\"09.9999.get_int\"}}", - "format": "command", - "keep_prefix": false, - "must_insert": false, - "trigger": "get_int" - }, - "AgAAAMgLAAAAAAAAyAsAAAAAAAADAAAAZ2V0yAsAAAAAAADPCwAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAADLCwAAAAAAAMsLAAAAAAAAAAAAAAAA8L8" + "AgAAADABAAAAAAAAMAEAAAAAAAABAAAAMS8BAAAAAAAALwEAAAAAAAABAAAALg", + "AQAAAAAAAAABAAAAMQEAAAAAAAAxAQAAAAAAAAAAAAAAAPC/" ] - ] - }, - { - "file": "Makefile", - "settings": - { - "buffer_size": 533, - "encoding": "UTF-8", - "line_ending": "Unix" - } - }, - { - "file": "stacosys/core/mailer.py", + ], "settings": { - "buffer_size": 1087, + "buffer_size": 620, "encoding": "UTF-8", "line_ending": "Unix" }, "undo_stack": [ - [ - 11, - 1, - "insert", - { - "characters": "\n" - }, - "AgAAAEIEAAAAAAAAQwQAAAAAAAAAAAAAQwQAAAAAAABPBAAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAABCBAAAAAAAAEIEAAAAAAAAAAAAAAAA8L8" - ], - [ - 12, - 1, - "left_delete", - null, - "AQAAAEsEAAAAAAAASwQAAAAAAAAEAAAAICAgIA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAABPBAAAAAAAAE8EAAAAAAAAAAAAAAAA8L8" - ], - [ - 13, - 1, - "insert", - { - "characters": "else:" - }, - "BQAAAEsEAAAAAAAATAQAAAAAAAAAAAAATAQAAAAAAABNBAAAAAAAAAAAAABNBAAAAAAAAE4EAAAAAAAAAAAAAE4EAAAAAAAATwQAAAAAAAAAAAAATwQAAAAAAABQBAAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAABLBAAAAAAAAEsEAAAAAAAAAAAAAAAA8L8" - ], - [ - 14, - 1, - "insert", - { - "characters": "\n" - }, - "AwAAAFAEAAAAAAAAUQQAAAAAAAAAAAAAUQQAAAAAAABZBAAAAAAAAAAAAABZBAAAAAAAAF0EAAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAABQBAAAAAAAAFAEAAAAAAAAAAAAAAAA8L8" - ], [ 16, 1, "insert", { - "characters": "False" + "characters": "6" }, - "BQAAAF0EAAAAAAAAXgQAAAAAAAAAAAAAXgQAAAAAAABfBAAAAAAAAAAAAABfBAAAAAAAAGAEAAAAAAAAAAAAAGAEAAAAAAAAYQQAAAAAAAAAAAAAYQQAAAAAAABiBAAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAABdBAAAAAAAAF0EAAAAAAAAAAAAAAAA8L8" + "AgAAACIBAAAAAAAAIwEAAAAAAAAAAAAAIwEAAAAAAAAjAQAAAAAAAAEAAAA1", + "AQAAAAAAAAABAAAAIgEAAAAAAAAjAQAAAAAAAAAAAAAAAPC/" ], [ - 44, - 1, - "insert", - { - "characters": "s" - }, - "AQAAAB4CAAAAAAAAHwIAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAeAgAAAAAAAB4CAAAAAAAAAAAAAAAA8L8" - ], - [ - 53, - 1, - "left_delete", - null, - "AQAAAEQEAAAAAAAARAQAAAAAAAA1AAAAICAgICAgICBlbHNlOgogICAgICAgICAgICBGYWxzZQogICAgICAgIHJldHVybiBGYWxzZQo", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAABEBAAAAAAAAHkEAAAAAAAAAAAAAAAAAAA" - ], - [ - 56, + 25, 1, "right_delete", null, - "AQAAACwEAAAAAAAALAQAAAAAAAAXAAAAICAgICAgICAgICAgcmV0dXJuIFRydWU", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAsBAAAAAAAAEMEAAAAAAAA////////738" - ], - [ - 70, - 1, - "left_delete", - null, - "AQAAAB4CAAAAAAAAHgIAAAAAAAABAAAAcw", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAfAgAAAAAAAB8CAAAAAAAAAAAAAAAA8L8" + "AQAAAMIBAAAAAAAAwgEAAAAAAAACAAAALjU", + "AQAAAAAAAAABAAAAwgEAAAAAAADEAQAAAAAAAAAAAAAAAPC/" ], [ - 80, + 43, 1, - "reindent", - null, - "AQAAACsEAAAAAAAANwQAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAArBAAAAAAAACsEAAAAAAAAAAAAAAAA8L8" - ], - [ - 81, - 1, - "left_delete", + "right_delete", null, - "AQAAADMEAAAAAAAAMwQAAAAAAAAEAAAAICAgIA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAA3BAAAAAAAADcEAAAAAAAAAAAAAAAA8L8" + "AQAAAMIAAAAAAAAAwgAAAAAAAAAWAAAAYXBzY2hlZHVsZXIgPSAiXjMuNi4zIg", + "AQAAAAAAAAABAAAA2AAAAAAAAADCAAAAAAAAAAAAAAAAAAAA" ], [ - 82, + 44, 1, - "insert", - { - "characters": "reru" - }, - "BAAAADMEAAAAAAAANAQAAAAAAAAAAAAANAQAAAAAAAA1BAAAAAAAAAAAAAA1BAAAAAAAADYEAAAAAAAAAAAAADYEAAAAAAAANwQAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAzBAAAAAAAADMEAAAAAAAAAAAAAAAA8L8" - ], - [ - 83, - 2, "left_delete", null, - "AgAAADYEAAAAAAAANgQAAAAAAAABAAAAdTUEAAAAAAAANQQAAAAAAAABAAAAcg", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAA3BAAAAAAAADcEAAAAAAAAAAAAAAAA8L8" - ], - [ - 84, - 1, - "insert", - { - "characters": "turn" - }, - "BAAAADUEAAAAAAAANgQAAAAAAAAAAAAANgQAAAAAAAA3BAAAAAAAAAAAAAA3BAAAAAAAADgEAAAAAAAAAAAAADgEAAAAAAAAOQQAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAA1BAAAAAAAADUEAAAAAAAAAAAAAAAA8L8" + "AQAAAMEAAAAAAAAAwQAAAAAAAAABAAAACg", + "AQAAAAAAAAABAAAAwgAAAAAAAADCAAAAAAAAAAAAAAAAAPC/" ], [ - 85, + 74, 1, "insert", { - "characters": " True" + "characters": "7" }, - "BQAAADkEAAAAAAAAOgQAAAAAAAAAAAAAOgQAAAAAAAA7BAAAAAAAAAAAAAA7BAAAAAAAADwEAAAAAAAAAAAAADwEAAAAAAAAPQQAAAAAAAAAAAAAPQQAAAAAAAA+BAAAAAAAAAAAAAA", - "AgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAA5BAAAAAAAADkEAAAAAAAAAAAAAAAA8L8" - ] - ] - }, - { - "file": "stacosys/db/dao.py", - "settings": - { - "buffer_size": 1633, - "line_ending": "Unix" - }, - "undo_stack": - [ - ] - }, - { - "file": "stacosys.sublime-project", - "settings": - { - "buffer_size": 381, - "encoding": "UTF-8", - "line_ending": "Unix" - }, - "undo_stack": - [ - [ - 4, - 1, - "", - null, - "AgAAAMMAAAAAAAAAZQEAAAAAAAAAAAAAZQEAAAAAAABlAQAAAAAAAPMAAAAgICAgInNldHRpbmdzIjoKICAgIHsKICAgICAgICAiTFNQIjoKICAgICAgICB7CiAgICAgICAgICAgICJMU1AtcHlsc3AiOgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAic2V0dGluZ3MiOgogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICJweWxzcC5wbHVnaW5zLmplZGkuZW52aXJvbm1lbnQiOiAiLi8udmVudiIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0", - "AQAAAAAAAAABAAAAtgEAAAAAAADDAAAAAAAAAAAAAAAAAPC/" + "AgAAAC4BAAAAAAAALwEAAAAAAAAAAAAALwEAAAAAAAAvAQAAAAAAAAEAAAA0", + "AQAAAAAAAAABAAAALgEAAAAAAAAvAQAAAAAAAAAAAAAAAPC/" ], [ - 8, + 78, 1, - "paste", + "right_delete", null, - "AgAAACYBAAAAAAAAUwEAAAAAAAAAAAAAUwEAAAAAAABTAQAAAAAAABkAAAAvLyBQdXQgeW91ciBzZXR0aW5ncyBoZXJl", - "AQAAAAAAAAABAAAAJgEAAAAAAAA/AQAAAAAAAP///////+9/" + "AQAAAC8BAAAAAAAALwEAAAAAAAACAAAALjU", + "AQAAAAAAAAABAAAALwEAAAAAAAAxAQAAAAAAAAAAAAAAAPC/" ] ] - }, - { - "file": "stacosys/model/comment.py", - "settings": - { - "buffer_size": 471, - "line_ending": "Unix" - } - }, - { - "file": "stacosys/conf/config.py", - "settings": - { - "buffer_size": 2481, - "encoding": "UTF-8", - "line_ending": "Unix" - } } ], "build_system": "", @@ -366,13 +141,61 @@ "selected_items": [ [ - "diag", - "LSP: Goto Diagnostic in Project" + "comment", + "Toggle Comment" + ], + [ + "install", + "Package Control: Install Package" ], [ "lsp", "LSP: Toggle Diagnostics Panel" ], + [ + "rename", + "LSP: Rename" + ], + [ + "brea", + "Python Breakpoints: Clear All" + ], + [ + "comm", + "Toggle Comment" + ], + [ + "move", + "File: Move…" + ], + [ + "break", + "Python Breakpoints: Clear All" + ], + [ + "remove", + "Package Control: Remove Package" + ], + [ + "make", + "Build With: Make" + ], + [ + "python", + "Python Breakpoints: Toggle" + ], + [ + "insta", + "Package Control: Install Package" + ], + [ + "diag", + "LSP: Toggle Diagnostics Panel" + ], + [ + "docstr", + "AutoDocstring: Current" + ], [ "Package Control: ", "Package Control: Install Package" @@ -385,10 +208,6 @@ "upg", "Package Control: Upgrade Package" ], - [ - "comment", - "Toggle Comment" - ], [ "form", "LSP: Format Document" @@ -409,10 +228,6 @@ "instal", "Package Control: Install Package" ], - [ - "install", - "Package Control: Install Package" - ], [ "package re", "Package Control: Remove Package" @@ -421,14 +236,6 @@ "paka", "Package Control: Remove Package" ], - [ - "remove", - "Package Control: Remove Package" - ], - [ - "insta", - "Package Control: Install Package" - ], [ "inst", "Package Control: Install Package" @@ -459,35 +266,72 @@ "expanded_folders": [ "/home/yannic/work/stacosys", + "/home/yannic/work/stacosys/.github", + "/home/yannic/work/stacosys/.github/workflows", + "/home/yannic/work/stacosys/docker", "/home/yannic/work/stacosys/stacosys", - "/home/yannic/work/stacosys/stacosys/conf", - "/home/yannic/work/stacosys/stacosys/core", "/home/yannic/work/stacosys/stacosys/db", "/home/yannic/work/stacosys/stacosys/interface", "/home/yannic/work/stacosys/stacosys/interface/web", "/home/yannic/work/stacosys/stacosys/model", + "/home/yannic/work/stacosys/stacosys/service", "/home/yannic/work/stacosys/tests" ], "file_history": [ - "/home/yannic/.cache/sublime-text-3/Package Storage/LSP-pyright/18.7.0/language-server/node_modules/pyright/dist/typeshed-fallback/stdlib/sys.pyi", - "/home/yannic/work/stacosys/stacosys/conf/config.py", + "/home/yannic/work/stacosys/stacosys/interface/form.py", + "/home/yannic/work/stacosys/.venv/lib/python3.11/site-packages/background.py", + "/home/yannic/work/stacosys/config-dev.ini", + "/home/yannic/work/stacosys/stacosys/service/mail.py", + "/home/yannic/work/stacosys/tests/test_mail.py", + "/home/yannic/work/stacosys/Makefile", + "/home/yannic/work/stacosys/tests/test_db.py", + "/home/yannic/.cache/sublime-text-3/Package Storage/LSP-pyright/18.7.0/language-server/node_modules/pyright/dist/typeshed-fallback/stdlib/builtins.pyi", + "/home/yannic/work/stacosys/stacosys/run.py", + "/home/yannic/work/stacosys/tests/test_config.py", + "/home/yannic/work/stacosys/.venv/lib64/python3.11/site-packages/mypy/typeshed/stdlib/socket.pyi", + "/usr/lib64/python3.11/smtplib.py", + "/home/yannic/work/stacosys/stacosys/interface/web/admin.py", + "/home/yannic/work/stacosys/stacosys/service/configuration.py", + "/home/yannic/work/stacosys/config.ini", + "/home/yannic/work/stacosys/stacosys/service/rssfeed.py", + "/home/yannic/work/stacosys/comments.xml", + "/home/yannic/work/stacosys/tests/test_api.py", + "/home/yannic/work/stacosys/stacosys/service/__init__.py", + "/home/yannic/work/stacosys/.venv/lib/python3.11/site-packages/PyRSS2Gen.py", + "/home/yannic/work/blog/README.md", + "/home/yannic/work/blog/Dockerfile", "/usr/lib64/python3.11/logging/__init__.py", + "/home/yannic/work/stacosys/stacosys/core/__init__.py", + "/home/yannic/work/stacosys/stacosys/core/configuration.py", + "/home/yannic/work/stacosys/.pylintrc", + "/home/yannic/work/stacosys/tests/test_form.py", + "/home/yannic/work/stacosys/stacosys/core/mailer.py", + "/home/yannic/work/stacosys/stacosys/conf/config.py", + "/home/yannic/work/stacosys/stacosys/core/rss.py", + "/home/yannic/work/stacosys/stacosys/db/dao.py", + "/home/yannic/work/stacosys/stacosys/db/database.py", + "/home/yannic/work/stacosys/run.sh", + "/home/yannic/work/stacosys/stacosys/interface/__init__.py", + "/home/yannic/work/stacosys/stacosys/interface/api.py", "/home/yannic/work/stacosys/stacosys.sublime-project", + "/home/yannic/work/stacosys/stacosys/model/comment.py", + "/home/yannic/work/stacosys/pylintrc", + "/home/yannic/work/stacosys/.venv/lib/python3.11/site-packages/flask/app.py", + "/home/yannic/.cache/sublime-text-3/Package Storage/LSP-pyright/18.7.0/language-server/node_modules/pyright/dist/typeshed-fallback/stdlib/sys.pyi", "/home/yannic/work/stacosys/flake8.ini", - "/home/yannic/work/stacosys/Makefile", - "/home/yannic/work/stacosys/stacosys/__init__.py", - "/home/yannic/work/stacosys/stacosys/core/rss.py" + "/home/yannic/work/stacosys/stacosys/__init__.py" ], "find": { - "height": 28.0 + "height": 40.0 }, "find_in_files": { - "height": 0.0, + "height": 104.0, "where_history": [ + "" ] }, "find_state": @@ -495,6 +339,12 @@ "case_sensitive": false, "find_history": [ + "apscheduler", + "_lang", + "config", + "SITE_TOKE", + "app.conf", + "disable", "background" ], "highlight": true, @@ -519,351 +369,32 @@ [ { "buffer": 0, - "file": "run.sh", - "semi_transient": false, - "settings": - { - "buffer_size": 40, - "regions": - { - }, - "selection": - [ - [ - 27, - 27 - ] - ], - "settings": - { - "lsp_uri": "file:///home/yannic/work/stacosys/run.sh", - "syntax": "Packages/ShellScript/Bash.sublime-syntax" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 7, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 1, - "file": "stacosys/run.py", - "semi_transient": false, - "settings": - { - "buffer_size": 3298, - "regions": - { - }, - "selection": - [ - [ - 1918, - 1918 - ] - ], - "settings": - { - "auto_complete_triggers": - [ - { - "characters": "<", - "selector": "text.html, text.xml" - }, - { - "rhs_empty": true, - "selector": "punctuation.accessor" - }, - { - "characters": ".[", - "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", - "server": "LSP-pyright" - } - ], - "lsp_active": true, - "lsp_hover_provider_count": 10, - "lsp_uri": "file:///home/yannic/work/stacosys/stacosys/run.py", - "show_definitions": false, - "syntax": "Packages/Python/Python.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 964.0, - "zoom_level": 1.0 - }, - "stack_index": 6, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 2, - "file": "Makefile", - "semi_transient": false, - "settings": - { - "buffer_size": 533, - "regions": - { - }, - "selection": - [ - [ - 334, - 334 - ] - ], - "settings": - { - "lsp_uri": "file:///home/yannic/work/stacosys/Makefile", - "syntax": "Packages/Makefile/Makefile.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": false - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 1, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 3, - "file": "stacosys/core/mailer.py", + "file": "pyproject.toml", "selected": true, "semi_transient": false, "settings": { - "buffer_size": 1087, - "regions": - { - }, - "selection": - [ - [ - 1086, - 1086 - ] - ], - "settings": - { - "auto_complete_triggers": - [ - { - "characters": "<", - "selector": "text.html, text.xml" - }, - { - "rhs_empty": true, - "selector": "punctuation.accessor" - }, - { - "characters": ".[", - "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", - "server": "LSP-pyright" - } - ], - "lsp_active": true, - "lsp_hover_provider_count": 10, - "lsp_uri": "file:///home/yannic/work/stacosys/stacosys/core/mailer.py", - "show_definitions": false, - "syntax": "Packages/Python/Python.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 399.0, - "zoom_level": 1.0 - }, - "stack_index": 0, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 4, - "file": "stacosys/db/dao.py", - "semi_transient": true, - "settings": - { - "buffer_size": 1633, + "buffer_size": 620, "regions": { }, "selection": [ [ - 524, - 524 + 481, + 481 ] ], "settings": { - "auto_complete_triggers": - [ - { - "characters": "<", - "selector": "text.html, text.xml" - }, - { - "rhs_empty": true, - "selector": "punctuation.accessor" - }, - { - "characters": ".[", - "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", - "server": "LSP-pyright" - } - ], - "lsp_active": true, - "lsp_hover_provider_count": 1, - "lsp_uri": "file:///home/yannic/work/stacosys/stacosys/db/dao.py", - "show_definitions": false, - "syntax": "Packages/Python/Python.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": true + "lsp_uri": "file:///home/yannic/work/stacosys/pyproject.toml", + "syntax": "Packages/Text/Plain text.tmLanguage" }, "translation.x": 0.0, "translation.y": 0.0, "zoom_level": 1.0 }, - "stack_index": 3, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 5, - "file": "stacosys.sublime-project", - "semi_transient": false, - "settings": - { - "buffer_size": 381, - "regions": - { - }, - "selection": - [ - [ - 364, - 364 - ] - ], - "settings": - { - "lsp_uri": "file:///home/yannic/work/stacosys/stacosys.sublime-project", - "syntax": "Packages/JSON/JSON.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 2, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 6, - "file": "stacosys/model/comment.py", - "semi_transient": false, - "settings": - { - "buffer_size": 471, - "regions": - { - }, - "selection": - [ - [ - 150, - 150 - ] - ], - "settings": - { - "auto_complete_triggers": - [ - { - "characters": "<", - "selector": "text.html, text.xml" - }, - { - "rhs_empty": true, - "selector": "punctuation.accessor" - }, - { - "characters": ".[", - "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", - "server": "LSP-pyright" - } - ], - "lsp_active": true, - "lsp_hover_provider_count": 6, - "lsp_uri": "file:///home/yannic/work/stacosys/stacosys/model/comment.py", - "show_definitions": false, - "syntax": "Packages/Python/Python.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 4, - "stack_multiselect": false, - "type": "text" - }, - { - "buffer": 7, - "file": "stacosys/conf/config.py", - "semi_transient": false, - "settings": - { - "buffer_size": 2481, - "regions": - { - }, - "selection": - [ - [ - 0, - 0 - ] - ], - "settings": - { - "auto_complete_triggers": - [ - { - "characters": "<", - "selector": "text.html, text.xml" - }, - { - "rhs_empty": true, - "selector": "punctuation.accessor" - }, - { - "characters": ".[", - "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", - "server": "LSP-pyright" - } - ], - "lsp_active": true, - "lsp_hover_provider_count": 6, - "lsp_uri": "file:///home/yannic/work/stacosys/stacosys/conf/config.py", - "show_definitions": false, - "syntax": "Packages/Python/Python.sublime-syntax", - "tab_size": 4, - "translate_tabs_to_spaces": true - }, - "translation.x": 0.0, - "translation.y": 1539.0, - "zoom_level": 1.0 - }, - "stack_index": 5, + "stack_index": 0, "stack_multiselect": false, "type": "text" } @@ -876,7 +407,7 @@ }, "input": { - "height": 0.0 + "height": 40.0 }, "layout": { @@ -909,6 +440,10 @@ { "height": 261.0 }, + "output.exec": + { + "height": 132.0 + }, "output.find_results": { "height": 0.0 @@ -938,17 +473,29 @@ "last_filter": "", "selected_items": [ + [ + "socket.p", + ".venv/lib64/python3.11/site-packages/mypy/typeshed/stdlib/socket.pyi" + ], + [ + "mail", + "stacosys/service/mail.py" + ], + [ + "conf", + "stacosys/service/configuration.py" + ] ], "width": 0.0 }, "select_project": { - "height": 0.0, + "height": 500.0, "last_filter": "", "selected_items": [ ], - "width": 0.0 + "width": 380.0 }, "select_symbol": { @@ -967,7 +514,7 @@ "show_open_files": false, "show_tabs": true, "side_bar_visible": true, - "side_bar_width": 311.0, + "side_bar_width": 299.0, "status_bar_visible": true, "template_settings": { diff --git a/stacosys/core/mailer.py b/stacosys/core/mailer.py deleted file mode 100644 index 43ec706..0000000 --- a/stacosys/core/mailer.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import logging -import smtplib -import ssl -from email.mime.text import MIMEText - -logger = logging.getLogger(__name__) - - -class Mailer: - def __init__( - self, - smtp_host, - smtp_port, - smtp_login, - smtp_password, - site_admin_email, - ): - self._smtp_host = smtp_host - self._smtp_port = smtp_port - self._smtp_login = smtp_login - self._smtp_password = smtp_password - self._site_admin_email = site_admin_email - - def send(self, subject, message): - sender = self._smtp_login - receivers = [self._site_admin_email] - - msg = MIMEText(message) - msg["Subject"] = subject - msg["To"] = self._site_admin_email - msg["From"] = sender - - context = ssl.create_default_context() - # TODO catch SMTP failure - with smtplib.SMTP_SSL( - self._smtp_host, self._smtp_port, context=context - ) as server: - server.login(self._smtp_login, self._smtp_password) - server.send_message(msg, sender, receivers) - return True diff --git a/stacosys/core/rss.py b/stacosys/core/rss.py deleted file mode 100644 index 42f9b8d..0000000 --- a/stacosys/core/rss.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -from datetime import datetime - -import markdown -import PyRSS2Gen - -from stacosys.model.comment import Comment - - -class Rss: - def __init__( - self, - lang, - rss_file, - rss_proto, - site_name, - site_url, - ): - self._lang = lang - self._rss_file = rss_file - self._rss_proto = rss_proto - self._site_name = site_name - self._site_url = site_url - - def generate(self): - md = markdown.Markdown() - - items = [] - for row in ( - Comment.select() - .where(Comment.published) - .order_by(-Comment.published) - .limit(10) - ): - item_link = "%s://%s%s" % ( - self._rss_proto, - self._site_url, - row.url, - ) - items.append( - PyRSS2Gen.RSSItem( - title="%s - %s://%s%s" - % ( - self._rss_proto, - row.author_name, - self._site_url, - row.url, - ), - link=item_link, - description=md.convert(row.content), - guid=PyRSS2Gen.Guid("%s/%d" % (item_link, row.id)), - pubDate=row.published, - ) - ) - - rss_title = 'Commentaires du site "%s"' % self._site_name - rss = PyRSS2Gen.RSS2( - title=rss_title, - link="%s://%s" % (self._rss_proto, self._site_url), - description=rss_title, - lastBuildDate=datetime.now(), - items=items, - ) - rss.write_xml(open(self._rss_file, "w"), encoding="utf-8") diff --git a/stacosys/interface/api.py b/stacosys/interface/api.py index 8c88a2e..06aa07f 100644 --- a/stacosys/interface/api.py +++ b/stacosys/interface/api.py @@ -38,5 +38,6 @@ def query_comments(): @app.route("/api/comments/count", methods=["GET"]) def get_comments_count(): + # TODO process pending comments url = request.args.get("url", "") return jsonify({"count": dao.count_published_comments(url)}) diff --git a/stacosys/interface/form.py b/stacosys/interface/form.py index 04b52b3..20722f9 100644 --- a/stacosys/interface/form.py +++ b/stacosys/interface/form.py @@ -7,6 +7,8 @@ from flask import abort, redirect, request from stacosys.db import dao from stacosys.interface import app +from stacosys.service import config, mailer +from stacosys.service.configuration import ConfigParameter logger = logging.getLogger(__name__) @@ -46,7 +48,7 @@ def new_form_comment(): # send notification e-mail asynchronously submit_new_comment(comment) - return redirect(app.config.get("SITE_REDIRECT"), code=302) + return redirect(config.get(ConfigParameter.SITE_REDIRECT), code=302) def check_form_data(posted_comment): @@ -57,7 +59,7 @@ def check_form_data(posted_comment): @background.task def submit_new_comment(comment): - site_url = app.config.get("SITE_URL") + site_url = config.get(ConfigParameter.SITE_URL) comment_list = ( f"Web admin interface: {site_url}/web/admin", "", @@ -72,17 +74,11 @@ def submit_new_comment(comment): email_body = "\n".join(comment_list) # send email to notify admin - subject = "STACOSYS " + app.config.get("SITE_NAME") - if app.config.get("MAILER").send(subject, email_body): + site_name = config.get(ConfigParameter.SITE_NAME) + subject = f"STACOSYS {site_name}" + if mailer.send(subject, email_body): logger.debug("new comment processed") - # save notification datetime dao.notify_comment(comment) else: logger.warning("rescheduled. send mail failure %s", subject) - - -@background.callback -def submit_new_comment_callback(future): - # TODO use future to log submit status - logger.debug(future) diff --git a/stacosys/interface/web/admin.py b/stacosys/interface/web/admin.py index 26d172f..8d81e3c 100644 --- a/stacosys/interface/web/admin.py +++ b/stacosys/interface/web/admin.py @@ -8,6 +8,8 @@ from flask import flash, redirect, render_template, request, session from stacosys.db import dao from stacosys.interface import app +from stacosys.service import config, rss +from stacosys.service.configuration import ConfigParameter logger = logging.getLogger(__name__) @@ -23,8 +25,8 @@ def index(): def is_login_ok(username, password): hashed = hashlib.sha256(password.encode()).hexdigest().upper() return ( - app.config.get("WEB_USERNAME") == username - and app.config.get("WEB_PASSWORD") == hashed + config.get(ConfigParameter.WEB_USERNAME) == username + and config.get(ConfigParameter.WEB_PASSWORD) == hashed ) @@ -40,7 +42,7 @@ def login(): flash("Identifiant ou mot de passe incorrect") return redirect("/web/login") # GET - return render_template("login_" + app.config.get("LANG", "fr") + ".html") + return render_template("login_" + config.get(ConfigParameter.LANG) + ".html") @app.route("/web/logout", methods=["GET"]) @@ -51,16 +53,19 @@ def logout(): @app.route("/web/admin", methods=["GET"]) def admin_homepage(): - if not ("user" in session and session["user"] == app.config.get("WEB_USERNAME")): + if not ( + "user" in session + and session["user"] == config.get(ConfigParameter.WEB_USERNAME) + ): # TODO localization flash("Vous avez été déconnecté.") return redirect("/web/login") comments = dao.find_not_published_comments() return render_template( - "admin_" + app.config.get("LANG", "fr") + ".html", + "admin_" + config.get(ConfigParameter.LANG) + ".html", comments=comments, - baseurl=app.config.get("SITE_URL"), + baseurl=config.get(ConfigParameter.SITE_URL), ) @@ -72,7 +77,7 @@ def admin_action(): flash("Commentaire introuvable") elif request.form.get("action") == "APPROVE": dao.publish_comment(comment) - app.config.get("RSS").generate() + rss.generate() # TODO localization flash("Commentaire publié") else: diff --git a/stacosys/run.py b/stacosys/run.py index 197215b..8bd26ad 100644 --- a/stacosys/run.py +++ b/stacosys/run.py @@ -6,12 +6,11 @@ import logging import os import sys -from stacosys.conf.config import Config, ConfigParameter -from stacosys.core.mailer import Mailer -from stacosys.core.rss import Rss from stacosys.db import database from stacosys.interface import api, app, form from stacosys.interface.web import admin +from stacosys.service import config, mailer, rss +from stacosys.service.configuration import ConfigParameter # configure logging @@ -30,23 +29,22 @@ def stacosys_server(config_pathname): logger = logging.getLogger(__name__) configure_logging(logging.INFO) logging.getLogger("werkzeug").level = logging.WARNING - logging.getLogger("apscheduler.executors").level = logging.WARNING # check config file exists if not os.path.isfile(config_pathname): logger.error("Configuration file '%s' not found.", config_pathname) sys.exit(1) - # load config - conf = Config.load(config_pathname) - is_config_ok, erreur_config = conf.check() + # load and check config + config.load(config_pathname) + is_config_ok, erreur_config = config.check() if not is_config_ok: logger.error("Configuration incorrecte '%s'", erreur_config) sys.exit(1) - logger.info(conf) + logger.info(config) # check database file exists (prevents from creating a fresh db) - db_pathname = conf.get(ConfigParameter.DB_SQLITE_FILE) + db_pathname = config.get(ConfigParameter.DB_SQLITE_FILE) if not db_pathname or not os.path.isfile(db_pathname): logger.error("Database file '%s' not found.", db_pathname) sys.exit(1) @@ -57,39 +55,30 @@ def stacosys_server(config_pathname): logger.info("Start Stacosys application") # generate RSS - rss = Rss( - conf.get(ConfigParameter.LANG), - conf.get(ConfigParameter.RSS_FILE), - conf.get(ConfigParameter.RSS_PROTO), - conf.get(ConfigParameter.SITE_NAME), - conf.get(ConfigParameter.SITE_URL), + rss.configure( + config.get(ConfigParameter.RSS_FILE), + config.get(ConfigParameter.SITE_NAME), + config.get(ConfigParameter.SITE_PROTO), + config.get(ConfigParameter.SITE_URL), ) rss.generate() # configure mailer - mailer = Mailer( - conf.get(ConfigParameter.SMTP_HOST), - conf.get_int(ConfigParameter.SMTP_PORT), - conf.get(ConfigParameter.SMTP_LOGIN), - conf.get(ConfigParameter.SMTP_PASSWORD), - conf.get(ConfigParameter.SITE_ADMIN_EMAIL), + mailer.configure_smtp( + config.get(ConfigParameter.SMTP_HOST), + config.get_int(ConfigParameter.SMTP_PORT), + config.get(ConfigParameter.SMTP_LOGIN), + config.get(ConfigParameter.SMTP_PASSWORD), ) + mailer.configure_destination(config.get(ConfigParameter.SITE_ADMIN_EMAIL)) + mailer.check() - # inject config parameters into flask - app.config.update(LANG=conf.get(ConfigParameter.LANG)) - app.config.update(SITE_NAME=conf.get(ConfigParameter.SITE_NAME)) - app.config.update(SITE_URL=conf.get(ConfigParameter.SITE_URL)) - app.config.update(SITE_REDIRECT=conf.get(ConfigParameter.SITE_REDIRECT)) - app.config.update(WEB_USERNAME=conf.get(ConfigParameter.WEB_USERNAME)) - app.config.update(WEB_PASSWORD=conf.get(ConfigParameter.WEB_PASSWORD)) - app.config.update(MAILER=mailer) - app.config.update(RSS=rss) logger.info("start interfaces %s %s %s", api, form, admin) # start Flask app.run( - host=conf.get(ConfigParameter.HTTP_HOST), - port=conf.get_int(ConfigParameter.HTTP_PORT), + host=config.get(ConfigParameter.HTTP_HOST), + port=config.get_int(ConfigParameter.HTTP_PORT), debug=False, use_reloader=False, ) diff --git a/stacosys/service/__init__.py b/stacosys/service/__init__.py new file mode 100644 index 0000000..6fcc80a --- /dev/null +++ b/stacosys/service/__init__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from .configuration import Config +from .mail import Mailer +from .rssfeed import Rss + +config = Config() +mailer = Mailer() +rss = Rss() diff --git a/stacosys/conf/config.py b/stacosys/service/configuration.py similarity index 76% rename from stacosys/conf/config.py rename to stacosys/service/configuration.py index 78adea8..85b3db8 100644 --- a/stacosys/conf/config.py +++ b/stacosys/service/configuration.py @@ -12,7 +12,6 @@ class ConfigParameter(Enum): HTTP_HOST = "http.host" HTTP_PORT = "http.port" - RSS_PROTO = "rss.proto" RSS_FILE = "rss.file" SMTP_HOST = "smtp.host" @@ -20,6 +19,7 @@ class ConfigParameter(Enum): SMTP_LOGIN = "smtp.login" SMTP_PASSWORD = "smtp.password" + SITE_PROTO = "site.proto" SITE_NAME = "site.name" SITE_URL = "site.url" SITE_ADMIN_EMAIL = "site.admin_email" @@ -30,14 +30,16 @@ class ConfigParameter(Enum): class Config: - def __init__(self): - self._cfg = configparser.ConfigParser() - @classmethod - def load(cls, config_pathname): - config = cls() - config._cfg.read(config_pathname) - return config + _cfg = configparser.ConfigParser() + + # def __new__(cls): + # if not hasattr(cls, "instance"): + # cls.instance = super(Config, cls).__new__(cls) + # return cls.instance + + def load(self, config_pathname): + self._cfg.read(config_pathname) def _split_key(self, key: ConfigParameter): section, param = str(key.value).split(".") @@ -50,12 +52,12 @@ class Config: section, param = self._split_key(key) return self._cfg.has_option(section, param) - def get(self, key: ConfigParameter): + def get(self, key: ConfigParameter) -> str: section, param = self._split_key(key) return ( self._cfg.get(section, param) if self._cfg.has_option(section, param) - else None + else "" ) def put(self, key: ConfigParameter, value): @@ -64,11 +66,11 @@ class Config: self._cfg.add_section(section) self._cfg.set(section, param, str(value)) - def get_int(self, key: ConfigParameter): + def get_int(self, key: ConfigParameter) -> int: value = self.get(key) return int(value) if value else 0 - def get_bool(self, key: ConfigParameter): + def get_bool(self, key: ConfigParameter) -> bool: value = self.get(key) assert value in ( "yes", @@ -85,8 +87,8 @@ class Config: return (True, None) def __repr__(self): - d = dict() + dict_repr = {} for section in self._cfg.sections(): for option in self._cfg.options(section): - d[".".join([section, option])] = self._cfg.get(section, option) - return str(d) + dict_repr[".".join([section, option])] = self._cfg.get(section, option) + return str(dict_repr) diff --git a/stacosys/service/mail.py b/stacosys/service/mail.py new file mode 100644 index 0000000..d7fe5ca --- /dev/null +++ b/stacosys/service/mail.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import logging +import smtplib +import ssl +from email.mime.text import MIMEText + +logger = logging.getLogger(__name__) + + +class Mailer: + def __init__(self) -> None: + self._smtp_host: str = "" + self._smtp_port: int = 0 + self._smtp_login: str = "" + self._smtp_password: str = "" + self._site_admin_email: str = "" + + def configure_smtp( + self, + smtp_host, + smtp_port, + smtp_login, + smtp_password, + ) -> None: + self._smtp_host = smtp_host + self._smtp_port = smtp_port + self._smtp_login = smtp_login + self._smtp_password = smtp_password + + def configure_destination(self, site_admin_email) -> None: + self._site_admin_email = site_admin_email + + def check(self): + server = smtplib.SMTP_SSL( + self._smtp_host, self._smtp_port, context=ssl.create_default_context() + ) + server.login(self._smtp_login, self._smtp_password) + server.close() + + def send(self, subject, message) -> bool: + sender = self._smtp_login + receivers = [self._site_admin_email] + + msg = MIMEText(message) + msg["Subject"] = subject + msg["To"] = self._site_admin_email + msg["From"] = sender + + # pylint: disable=bare-except + try: + server = smtplib.SMTP_SSL( + self._smtp_host, self._smtp_port, context=ssl.create_default_context() + ) + server.login(self._smtp_login, self._smtp_password) + server.send_message(msg, sender, receivers) + server.close() + success = True + except: + success = False + return success diff --git a/stacosys/service/rssfeed.py b/stacosys/service/rssfeed.py new file mode 100644 index 0000000..3e81389 --- /dev/null +++ b/stacosys/service/rssfeed.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from datetime import datetime + +import markdown +import PyRSS2Gen + +from stacosys.model.comment import Comment + + +class Rss: + def __init__(self) -> None: + self._rss_file: str = "" + self._site_proto: str = "" + self._site_name: str = "" + self._site_url: str = "" + + def configure( + self, + rss_file, + site_name, + site_proto, + site_url, + ) -> None: + self._rss_file = rss_file + self._site_name = site_name + self._site_proto = site_proto + self._site_url = site_url + + def generate(self) -> None: + markdownizer = markdown.Markdown() + + items = [] + for row in ( + Comment.select() + .where(Comment.published) + .order_by(-Comment.published) + .limit(10) + ): + item_link = f"{self._site_proto}://{self._site_url}{row.url}" + items.append( + PyRSS2Gen.RSSItem( + title=f"{self._site_proto}://{self._site_url}{row.url} - {row.author_name}", + link=item_link, + description=markdownizer.convert(row.content), + guid=PyRSS2Gen.Guid(f"{item_link}{row.id}"), + pubDate=row.published, + ) + ) + + rss_title = f"Commentaires du site {self._site_name}" + rss = PyRSS2Gen.RSS2( + title=rss_title, + link=f"{self._site_proto}://{self._site_url}", + description=rss_title, + lastBuildDate=datetime.now(), + items=items, + ) + # TODO technical debt: replace pyRss2Gen + # TODO validate feed (https://validator.w3.org/feed/check.cgi) + # pylint: disable=consider-using-with + rss.write_xml(open(self._rss_file, "w", encoding="utf-8"), encoding="utf-8") diff --git a/tests/test_api.py b/tests/test_api.py index 6cb2196..8efd052 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -25,7 +25,6 @@ def client(): logger = logging.getLogger(__name__) database.setup(":memory:") init_test_db() - app.config.update(SITE_TOKEN="ETC") logger.info(f"start interface {api}") return app.test_client() diff --git a/tests/test_config.py b/tests/test_config.py index e098965..db884c8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,41 +1,34 @@ #!/usr/bin/python # -*- coding: UTF-8 -*- -import unittest +import pytest -from stacosys.conf.config import Config, ConfigParameter +from stacosys.service import config +from stacosys.service.configuration import ConfigParameter EXPECTED_DB_SQLITE_FILE = "db.sqlite" EXPECTED_HTTP_PORT = 8080 EXPECTED_LANG = "fr" -class ConfigTestCase(unittest.TestCase): - def setUp(self): - self.conf = Config() - self.conf.put(ConfigParameter.DB_SQLITE_FILE, EXPECTED_DB_SQLITE_FILE) - self.conf.put(ConfigParameter.HTTP_PORT, EXPECTED_HTTP_PORT) - - def test_exists(self): - self.assertTrue(self.conf.exists(ConfigParameter.DB_SQLITE_FILE)) - - def test_get(self): - self.assertEqual( - self.conf.get(ConfigParameter.DB_SQLITE_FILE), EXPECTED_DB_SQLITE_FILE - ) - self.assertIsNone(self.conf.get(ConfigParameter.HTTP_HOST)) - self.assertEqual( - self.conf.get(ConfigParameter.HTTP_PORT), str(EXPECTED_HTTP_PORT) - ) - self.assertEqual(self.conf.get_int(ConfigParameter.HTTP_PORT), 8080) - try: - self.conf.get_bool(ConfigParameter.DB_SQLITE_FILE) - self.assertTrue(False) - except AssertionError: - pass - - def test_put(self): - self.assertFalse(self.conf.exists(ConfigParameter.LANG)) - self.conf.put(ConfigParameter.LANG, EXPECTED_LANG) - self.assertTrue(self.conf.exists(ConfigParameter.LANG)) - self.assertEqual(self.conf.get(ConfigParameter.LANG), EXPECTED_LANG) +@pytest.fixture +def init_config(): + config.put(ConfigParameter.DB_SQLITE_FILE, EXPECTED_DB_SQLITE_FILE) + config.put(ConfigParameter.HTTP_PORT, EXPECTED_HTTP_PORT) + +def test_exists(init_config): + assert config.exists(ConfigParameter.DB_SQLITE_FILE) + +def test_get(init_config): + assert config.get(ConfigParameter.DB_SQLITE_FILE) == EXPECTED_DB_SQLITE_FILE + assert config.get(ConfigParameter.HTTP_HOST) == "" + assert config.get(ConfigParameter.HTTP_PORT) == str(EXPECTED_HTTP_PORT) + assert config.get_int(ConfigParameter.HTTP_PORT) == EXPECTED_HTTP_PORT + with pytest.raises(AssertionError): + config.get_bool(ConfigParameter.DB_SQLITE_FILE) + +def test_put(init_config): + assert not config.exists(ConfigParameter.LANG) + config.put(ConfigParameter.LANG, EXPECTED_LANG) + assert config.exists(ConfigParameter.LANG) + assert config.get(ConfigParameter.LANG) == EXPECTED_LANG diff --git a/tests/test_db.py b/tests/test_db.py index 99eeb7e..649c5e7 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -1,53 +1,53 @@ -import unittest +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import pytest from stacosys.db import dao from stacosys.db import database +@pytest.fixture +def setup_db(): + database.setup(":memory:") + + +def test_dao_published(setup_db): + + # test count published + assert 0 == dao.count_published_comments("") + c1 = dao.create_comment("/post1", "Yax", "", "", "Comment 1") + assert 0 == dao.count_published_comments("") + dao.publish_comment(c1) + assert 1 == dao.count_published_comments("") + c2 = dao.create_comment("/post2", "Yax", "", "", "Comment 2") + dao.publish_comment(c2) + assert 2 == dao.count_published_comments("") + c3 = dao.create_comment("/post2", "Yax", "", "", "Comment 3") + dao.publish_comment(c3) + assert 1 == dao.count_published_comments("/post1") + assert 2 == dao.count_published_comments("/post2") + + # test find published + assert 0 == len(dao.find_published_comments_by_url("/")) + assert 1 == len(dao.find_published_comments_by_url("/post1")) + assert 2 == len(dao.find_published_comments_by_url("/post2")) + + dao.delete_comment(c1) + assert 0 == len(dao.find_published_comments_by_url("/post1")) + +def test_dao_notified(setup_db): + + # test count notified + assert 0 == len(dao.find_not_notified_comments()) + c1 = dao.create_comment("/post1", "Yax", "", "", "Comment 1") + assert 1 == len(dao.find_not_notified_comments()) + c2 = dao.create_comment("/post2", "Yax", "", "", "Comment 2") + assert 2 == len(dao.find_not_notified_comments()) + dao.notify_comment(c1) + dao.notify_comment(c2) + assert 0 == len(dao.find_not_notified_comments()) + c3 = dao.create_comment("/post2", "Yax", "", "", "Comment 3") + assert 1 == len(dao.find_not_notified_comments()) + dao.notify_comment(c3) + assert 0 == len(dao.find_not_notified_comments()) -class DbTestCase(unittest.TestCase): - def setUp(self): - database.setup(":memory:") - - def test_dao_published(self): - - # test count published - self.assertEqual(0, dao.count_published_comments("")) - c1 = dao.create_comment("/post1", "Yax", "", "", "Comment 1") - self.assertEqual(0, dao.count_published_comments("")) - dao.publish_comment(c1) - self.assertEqual(1, dao.count_published_comments("")) - c2 = dao.create_comment("/post2", "Yax", "", "", "Comment 2") - dao.publish_comment(c2) - self.assertEqual(2, dao.count_published_comments("")) - c3 = dao.create_comment("/post2", "Yax", "", "", "Comment 3") - dao.publish_comment(c3) - self.assertEqual(1, dao.count_published_comments("/post1")) - self.assertEqual(2, dao.count_published_comments("/post2")) - - # test find published - self.assertEqual(0, len(dao.find_published_comments_by_url("/"))) - self.assertEqual(1, len(dao.find_published_comments_by_url("/post1"))) - self.assertEqual(2, len(dao.find_published_comments_by_url("/post2"))) - - dao.delete_comment(c1) - self.assertEqual(0, len(dao.find_published_comments_by_url("/post1"))) - - def test_dao_notified(self): - - # test count notified - self.assertEqual(0, len(dao.find_not_notified_comments())) - c1 = dao.create_comment("/post1", "Yax", "", "", "Comment 1") - self.assertEqual(1, len(dao.find_not_notified_comments())) - c2 = dao.create_comment("/post2", "Yax", "", "", "Comment 2") - self.assertEqual(2, len(dao.find_not_notified_comments())) - dao.notify_comment(c1) - dao.notify_comment(c2) - self.assertEqual(0, len(dao.find_not_notified_comments())) - c3 = dao.create_comment("/post2", "Yax", "", "", "Comment 3") - self.assertEqual(1, len(dao.find_not_notified_comments())) - dao.notify_comment(c3) - self.assertEqual(0, len(dao.find_not_notified_comments())) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_form.py b/tests/test_form.py index 55ebda8..dedef3a 100644 --- a/tests/test_form.py +++ b/tests/test_form.py @@ -13,8 +13,7 @@ from stacosys.interface import form @pytest.fixture def client(): logger = logging.getLogger(__name__) - database.setup(":memory:") - app.config.update(SITE_REDIRECT="/redirect") + database.setup(":memory:") logger.info(f"start interface {form}") return app.test_client() diff --git a/tests/test_mail.py b/tests/test_mail.py new file mode 100644 index 0000000..192c3cc --- /dev/null +++ b/tests/test_mail.py @@ -0,0 +1,11 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +import pytest +from stacosys.service import mailer + +def test_configure_and_check(): + mailer.configure_smtp("localhost", 2525, "admin", "admin") + mailer.configure_destination("admin@mydomain.com") + with pytest.raises(ConnectionRefusedError): + mailer.check() \ No newline at end of file diff --git a/tests/test_rssfeed.py b/tests/test_rssfeed.py new file mode 100644 index 0000000..713eead --- /dev/null +++ b/tests/test_rssfeed.py @@ -0,0 +1,7 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from stacosys.service import rss + +def test_configure(): + rss.configure("comments.xml", "blog", "http", "blog.mydomain.com") diff --git a/tox.ini b/tox.ini deleted file mode 100644 index c4ac41c..0000000 --- a/tox.ini +++ /dev/null @@ -1,9 +0,0 @@ -[tox] -isolated_build = true -envlist = py38, py39 - -[testenv] -whitelist_externals = poetry -commands = - poetry install -v - poetry run pytest tests/