move DB operations to DAO
This commit is contained in:
parent
3905ef4bbd
commit
2694ef1f75
8 changed files with 94 additions and 238 deletions
135
poetry.lock
generated
135
poetry.lock
generated
|
@ -32,28 +32,6 @@ tornado = ["tornado (>=4.3)"]
|
||||||
twisted = ["twisted"]
|
twisted = ["twisted"]
|
||||||
zookeeper = ["kazoo"]
|
zookeeper = ["kazoo"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomicwrites"
|
|
||||||
version = "1.4.0"
|
|
||||||
description = "Atomic file writes."
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "attrs"
|
|
||||||
version = "21.2.0"
|
|
||||||
description = "Classes Without Boilerplate"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
|
|
||||||
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
|
||||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
|
|
||||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "20.8b1"
|
version = "20.8b1"
|
||||||
|
@ -179,14 +157,6 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iniconfig"
|
|
||||||
version = "1.1.1"
|
|
||||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itsdangerous"
|
name = "itsdangerous"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
@ -260,17 +230,6 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "packaging"
|
|
||||||
version = "21.0"
|
|
||||||
description = "Core utilities for Python packages"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
pyparsing = ">=2.0.2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathspec"
|
name = "pathspec"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
@ -287,17 +246,6 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pluggy"
|
|
||||||
version = "0.13.1"
|
|
||||||
description = "plugin and hook calling mechanisms for python"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["pre-commit", "tox"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "profig"
|
name = "profig"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -306,14 +254,6 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "py"
|
|
||||||
version = "1.10.0"
|
|
||||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pycodestyle"
|
name = "pycodestyle"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
@ -330,14 +270,6 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyparsing"
|
|
||||||
version = "2.4.7"
|
|
||||||
description = "Python parsing module"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyrss2gen"
|
name = "pyrss2gen"
|
||||||
version = "1.1"
|
version = "1.1"
|
||||||
|
@ -346,27 +278,6 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pytest"
|
|
||||||
version = "6.2.4"
|
|
||||||
description = "pytest: simple powerful testing with Python"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
|
||||||
attrs = ">=19.2.0"
|
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
|
||||||
iniconfig = "*"
|
|
||||||
packaging = "*"
|
|
||||||
pluggy = ">=0.12,<1.0.0a1"
|
|
||||||
py = ">=1.8.2"
|
|
||||||
toml = "*"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.8.2"
|
version = "2.8.2"
|
||||||
|
@ -431,14 +342,6 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinydb"
|
|
||||||
version = "4.5.0"
|
|
||||||
description = "TinyDB is a tiny, document oriented database optimized for your happiness :)"
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.5,<4.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
@ -501,7 +404,7 @@ watchdog = ["watchdog"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "c2c76edb4fcae80581572bcb9699f0b8b0193cbcc5e08a4b6f431c15f09236e7"
|
content-hash = "8190054ee0a6bf5fccefd841ac71fa6851a4e7d057c5af6fb83ea2364711cb78"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
appdirs = [
|
appdirs = [
|
||||||
|
@ -512,14 +415,6 @@ apscheduler = [
|
||||||
{file = "APScheduler-3.7.0-py2.py3-none-any.whl", hash = "sha256:c06cc796d5bb9eb3c4f77727f6223476eb67749e7eea074d1587550702a7fbe3"},
|
{file = "APScheduler-3.7.0-py2.py3-none-any.whl", hash = "sha256:c06cc796d5bb9eb3c4f77727f6223476eb67749e7eea074d1587550702a7fbe3"},
|
||||||
{file = "APScheduler-3.7.0.tar.gz", hash = "sha256:1cab7f2521e107d07127b042155b632b7a1cd5e02c34be5a28ff62f77c900c6a"},
|
{file = "APScheduler-3.7.0.tar.gz", hash = "sha256:1cab7f2521e107d07127b042155b632b7a1cd5e02c34be5a28ff62f77c900c6a"},
|
||||||
]
|
]
|
||||||
atomicwrites = [
|
|
||||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
|
||||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
|
||||||
]
|
|
||||||
attrs = [
|
|
||||||
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
|
|
||||||
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
|
|
||||||
]
|
|
||||||
black = [
|
black = [
|
||||||
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
|
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
|
||||||
]
|
]
|
||||||
|
@ -558,10 +453,6 @@ idna = [
|
||||||
{file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
|
{file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
|
||||||
{file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
|
{file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
|
||||||
]
|
]
|
||||||
iniconfig = [
|
|
||||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
|
||||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
|
||||||
]
|
|
||||||
itsdangerous = [
|
itsdangerous = [
|
||||||
{file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
|
{file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
|
||||||
{file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
|
{file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
|
||||||
|
@ -634,10 +525,6 @@ mypy-extensions = [
|
||||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||||
]
|
]
|
||||||
packaging = [
|
|
||||||
{file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
|
|
||||||
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
|
|
||||||
]
|
|
||||||
pathspec = [
|
pathspec = [
|
||||||
{file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
|
{file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
|
||||||
{file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
|
{file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
|
||||||
|
@ -645,17 +532,9 @@ pathspec = [
|
||||||
peewee = [
|
peewee = [
|
||||||
{file = "peewee-3.14.4.tar.gz", hash = "sha256:9e356b327c2eaec6dd42ecea6f4ddded025793dba906a3d065a0452e726c51a2"},
|
{file = "peewee-3.14.4.tar.gz", hash = "sha256:9e356b327c2eaec6dd42ecea6f4ddded025793dba906a3d065a0452e726c51a2"},
|
||||||
]
|
]
|
||||||
pluggy = [
|
|
||||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
|
||||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
|
||||||
]
|
|
||||||
profig = [
|
profig = [
|
||||||
{file = "profig-0.5.1.tar.gz", hash = "sha256:cb9c094325a93505fc6325d13f3e679b281093223f143a96a6df8ad9c2bfc9a6"},
|
{file = "profig-0.5.1.tar.gz", hash = "sha256:cb9c094325a93505fc6325d13f3e679b281093223f143a96a6df8ad9c2bfc9a6"},
|
||||||
]
|
]
|
||||||
py = [
|
|
||||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
|
||||||
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
|
|
||||||
]
|
|
||||||
pycodestyle = [
|
pycodestyle = [
|
||||||
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
|
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
|
||||||
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
|
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
|
||||||
|
@ -664,17 +543,9 @@ pyflakes = [
|
||||||
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
|
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
|
||||||
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
|
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
|
||||||
]
|
]
|
||||||
pyparsing = [
|
|
||||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
|
||||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
|
||||||
]
|
|
||||||
pyrss2gen = [
|
pyrss2gen = [
|
||||||
{file = "PyRSS2Gen-1.1.tar.gz", hash = "sha256:7960aed7e998d2482bf58716c316509786f596426f879b05f8d84e98b82c6ee7"},
|
{file = "PyRSS2Gen-1.1.tar.gz", hash = "sha256:7960aed7e998d2482bf58716c316509786f596426f879b05f8d84e98b82c6ee7"},
|
||||||
]
|
]
|
||||||
pytest = [
|
|
||||||
{file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"},
|
|
||||||
{file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"},
|
|
||||||
]
|
|
||||||
python-dateutil = [
|
python-dateutil = [
|
||||||
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
||||||
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
||||||
|
@ -739,10 +610,6 @@ six = [
|
||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
tinydb = [
|
|
||||||
{file = "tinydb-4.5.0-py3-none-any.whl", hash = "sha256:ab2669b88ba1e1b3e1bd6da1a1e3ee284fde6fbf327fb16a206ac3954915f37f"},
|
|
||||||
{file = "tinydb-4.5.0.tar.gz", hash = "sha256:d287cd092f19a2b8553d0a6018f172c351268fb8619898eb87633d9e2c036344"},
|
|
||||||
]
|
|
||||||
toml = [
|
toml = [
|
||||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||||
|
|
|
@ -13,7 +13,6 @@ pyrss2gen = "^1.1"
|
||||||
profig = "^0.5.1"
|
profig = "^0.5.1"
|
||||||
markdown = "^3.1.1"
|
markdown = "^3.1.1"
|
||||||
flask_apscheduler = "^1.11.0"
|
flask_apscheduler = "^1.11.0"
|
||||||
tinydb = "^4.3.0"
|
|
||||||
Flask = "^2.0.1"
|
Flask = "^2.0.1"
|
||||||
peewee = "^3.14.0"
|
peewee = "^3.14.0"
|
||||||
requests = "^2.25.1"
|
requests = "^2.25.1"
|
||||||
|
@ -23,7 +22,6 @@ rope = "^0.16.0"
|
||||||
mypy = "^0.790"
|
mypy = "^0.790"
|
||||||
flake8-black = "^0.2.1"
|
flake8-black = "^0.2.1"
|
||||||
black = "^20.8b1"
|
black = "^20.8b1"
|
||||||
pytest = "^6.2.1"
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry>=0.12"]
|
requires = ["poetry>=0.12"]
|
||||||
|
|
|
@ -5,12 +5,13 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from stacosys.core.templater import Templater, Template
|
|
||||||
from stacosys.model.comment import Comment
|
|
||||||
from stacosys.model.email import Email
|
|
||||||
from stacosys.core.rss import Rss
|
|
||||||
from stacosys.core.mailer import Mailer
|
from stacosys.core.mailer import Mailer
|
||||||
|
from stacosys.core.rss import Rss
|
||||||
|
from stacosys.core.templater import Templater, Template
|
||||||
from stacosys.db import dao
|
from stacosys.db import dao
|
||||||
|
from stacosys.model.email import Email
|
||||||
|
|
||||||
|
REGEX_EMAIL_SUBJECT = r".*STACOSYS.*\[(\d+)\:(\w+)\]"
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -21,41 +22,42 @@ templater = Templater(template_path)
|
||||||
|
|
||||||
def fetch_mail_answers(lang, mailer: Mailer, rss: Rss, site_token):
|
def fetch_mail_answers(lang, mailer: Mailer, rss: Rss, site_token):
|
||||||
for msg in mailer.fetch():
|
for msg in mailer.fetch():
|
||||||
if re.search(r".*STACOSYS.*\[(\d+)\:(\w+)\]", msg.subject, re.DOTALL):
|
# filter stacosys e-mails
|
||||||
if _reply_comment_email(lang, mailer, rss, msg, site_token):
|
m = re.search(REGEX_EMAIL_SUBJECT, msg.subject, re.DOTALL)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
|
||||||
|
comment_id = int(m.group(1))
|
||||||
|
submitted_token = m.group(2)
|
||||||
|
|
||||||
|
# validate token
|
||||||
|
if submitted_token != site_token:
|
||||||
|
logger.warning("ignore corrupted email. Unknown token %d" % comment_id)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not msg.plain_text_content:
|
||||||
|
logger.warning("ignore empty email")
|
||||||
|
continue
|
||||||
|
|
||||||
|
_reply_comment_email(lang, mailer, rss, msg, comment_id)
|
||||||
mailer.delete(msg.id)
|
mailer.delete(msg.id)
|
||||||
|
|
||||||
|
|
||||||
def _reply_comment_email(lang, mailer: Mailer, rss: Rss, email: Email, site_token):
|
def _reply_comment_email(lang, mailer: Mailer, rss: Rss, email: Email, comment_id):
|
||||||
|
# retrieve comment
|
||||||
m = re.search(r"\[(\d+)\:(\w+)\]", email.subject)
|
comment = dao.find_comment_by_id(comment_id)
|
||||||
if not m:
|
|
||||||
logger.warning("ignore corrupted email. No token %s" % email.subject)
|
|
||||||
return
|
|
||||||
comment_id = int(m.group(1))
|
|
||||||
token = m.group(2)
|
|
||||||
if token != site_token:
|
|
||||||
logger.warning("ignore corrupted email. Unknown token %d" % comment_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
# retrieve site and comment rows
|
|
||||||
comment = Comment
|
|
||||||
if not comment:
|
if not comment:
|
||||||
logger.warning("unknown comment %d" % comment_id)
|
logger.warning("unknown comment %d" % comment_id)
|
||||||
return True
|
return
|
||||||
|
|
||||||
if comment.published:
|
if comment.published:
|
||||||
logger.warning("ignore already published email. token %d" % comment_id)
|
logger.warning("ignore already published email. token %d" % comment_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not email.plain_text_content:
|
|
||||||
logger.warning("ignore empty email")
|
|
||||||
return
|
|
||||||
|
|
||||||
# safe logic: no answer or unknown answer is a go for publishing
|
# safe logic: no answer or unknown answer is a go for publishing
|
||||||
if email.plain_text_content[:2].upper() == "NO":
|
if email.plain_text_content[:2].upper() == "NO":
|
||||||
logger.info("discard comment: %d" % comment_id)
|
logger.info("discard comment: %d" % comment_id)
|
||||||
comment.delete_instance()
|
dao.delete_comment(comment)
|
||||||
new_email_body = templater.get_template(lang, Template.DROP_COMMENT).render(
|
new_email_body = templater.get_template(lang, Template.DROP_COMMENT).render(
|
||||||
original=email.plain_text_content
|
original=email.plain_text_content
|
||||||
)
|
)
|
||||||
|
@ -63,7 +65,7 @@ def _reply_comment_email(lang, mailer: Mailer, rss: Rss, email: Email, site_toke
|
||||||
logger.warning("minor failure. cannot send rejection mail " + email.subject)
|
logger.warning("minor failure. cannot send rejection mail " + email.subject)
|
||||||
else:
|
else:
|
||||||
# save publishing datetime
|
# save publishing datetime
|
||||||
dao.publish(comment)
|
dao.publish_comment(comment)
|
||||||
logger.info("commit comment: %d" % comment_id)
|
logger.info("commit comment: %d" % comment_id)
|
||||||
|
|
||||||
# rebuild RSS
|
# rebuild RSS
|
||||||
|
@ -76,11 +78,9 @@ def _reply_comment_email(lang, mailer: Mailer, rss: Rss, email: Email, site_toke
|
||||||
if not mailer.send(email.from_addr, "Re: " + email.subject, new_email_body):
|
if not mailer.send(email.from_addr, "Re: " + email.subject, new_email_body):
|
||||||
logger.warning("minor failure. cannot send approval email " + email.subject)
|
logger.warning("minor failure. cannot send approval email " + email.subject)
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def submit_new_comment(lang, site_name, site_token, site_admin_email, mailer):
|
def submit_new_comment(lang, site_name, site_token, site_admin_email, mailer):
|
||||||
for comment in Comment.select().where(Comment.notified.is_null()):
|
for comment in dao.find_not_notified_comments():
|
||||||
comment_list = (
|
comment_list = (
|
||||||
"author: %s" % comment.author_name,
|
"author: %s" % comment.author_name,
|
||||||
"site: %s" % comment.author_site,
|
"site: %s" % comment.author_site,
|
||||||
|
@ -95,12 +95,12 @@ def submit_new_comment(lang, site_name, site_token, site_admin_email, mailer):
|
||||||
url=comment.url, comment=comment_text
|
url=comment.url, comment=comment_text
|
||||||
)
|
)
|
||||||
|
|
||||||
# send email
|
# send email to notify admin
|
||||||
subject = "STACOSYS %s: [%d:%s]" % (site_name, comment.id, site_token)
|
subject = "STACOSYS %s: [%d:%s]" % (site_name, comment.id, site_token)
|
||||||
if mailer.send(site_admin_email, subject, email_body):
|
if mailer.send(site_admin_email, subject, email_body):
|
||||||
logger.debug("new comment processed ")
|
logger.debug("new comment processed ")
|
||||||
|
|
||||||
# notify site admin and save notification datetime
|
# save notification datetime
|
||||||
dao.notify_site_admin(comment)
|
dao.notify_comment(comment)
|
||||||
else:
|
else:
|
||||||
logger.warning("rescheduled. send mail failure " + subject)
|
logger.warning("rescheduled. send mail failure " + subject)
|
||||||
|
|
|
@ -5,14 +5,49 @@ from datetime import datetime
|
||||||
from stacosys.model.comment import Comment
|
from stacosys.model.comment import Comment
|
||||||
|
|
||||||
|
|
||||||
def notify_site_admin(comment: Comment):
|
def find_comment_by_id(id):
|
||||||
|
return Comment.get_by_id(id)
|
||||||
|
|
||||||
|
|
||||||
|
def notify_comment(comment: Comment):
|
||||||
comment.notified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
comment.notified = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
|
|
||||||
def publish(comment: Comment):
|
def publish_comment(comment: Comment):
|
||||||
comment.published = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
comment.published = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_comment(comment: Comment):
|
||||||
|
comment.delete_instance()
|
||||||
|
|
||||||
|
|
||||||
|
def find_not_notified_comments():
|
||||||
|
return Comment.select().where(Comment.notified.is_null())
|
||||||
|
|
||||||
|
|
||||||
|
def find_published_comments_by_url(url):
|
||||||
|
return Comment.select(Comment).where((Comment.url == url) & (Comment.published.is_null(False))).order_by(
|
||||||
|
+Comment.published)
|
||||||
|
|
||||||
|
|
||||||
|
def count_published_comments(url):
|
||||||
|
return Comment.select(Comment).where(
|
||||||
|
(Comment.url == url) & (Comment.published.is_null(False))).count() if url else Comment.select(Comment).where(
|
||||||
|
Comment.publishd.is_null(False)).count()
|
||||||
|
|
||||||
|
|
||||||
|
def create_comment(url, author_name, author_site, author_gravatar, message):
|
||||||
|
created = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
comment = Comment(
|
||||||
|
url=url,
|
||||||
|
author_name=author_name,
|
||||||
|
author_site=author_site,
|
||||||
|
author_gravatar=author_gravatar,
|
||||||
|
content=message,
|
||||||
|
created=created,
|
||||||
|
notified=None,
|
||||||
|
published=None,
|
||||||
|
)
|
||||||
|
comment.save()
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: UTF-8 -*-
|
# -*- coding: UTF-8 -*-
|
||||||
|
|
||||||
import json
|
|
||||||
from peewee import Model
|
from peewee import Model
|
||||||
from playhouse.db_url import SqliteDatabase
|
from playhouse.db_url import SqliteDatabase
|
||||||
from playhouse.shortcuts import model_to_dict
|
|
||||||
from tinydb import TinyDB
|
|
||||||
|
|
||||||
db = SqliteDatabase(None)
|
db = SqliteDatabase(None)
|
||||||
|
|
||||||
|
@ -20,29 +17,8 @@ class Database:
|
||||||
return db
|
return db
|
||||||
|
|
||||||
def setup(self, db_url):
|
def setup(self, db_url):
|
||||||
|
|
||||||
db.init(db_url)
|
db.init(db_url)
|
||||||
db.connect()
|
db.connect()
|
||||||
|
|
||||||
from stacosys.model.comment import Comment
|
from stacosys.model.comment import Comment
|
||||||
db.create_tables([Comment], safe=True)
|
db.create_tables([Comment], safe=True)
|
||||||
|
|
||||||
|
|
||||||
# if config.exists(config.DB_BACKUP_JSON_FILE):
|
|
||||||
# _backup_db(config.DB_BACKUP_JSON_FILE, Comment)
|
|
||||||
|
|
||||||
|
|
||||||
def _tojson_model(comment):
|
|
||||||
dcomment = model_to_dict(comment)
|
|
||||||
# del dcomment["site"]
|
|
||||||
tcomment = json.dumps(dcomment, indent=4, sort_keys=True, default=str)
|
|
||||||
return json.loads(tcomment)
|
|
||||||
|
|
||||||
|
|
||||||
def _backup_db(db_file, Comment):
|
|
||||||
db = TinyDB(db_file, sort_keys=True, indent=4, separators=(",", ": "))
|
|
||||||
db.drop_tables()
|
|
||||||
table = db.table("comments")
|
|
||||||
for comment in Comment.select():
|
|
||||||
cc = _tojson_model(comment)
|
|
||||||
table.insert(cc)
|
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from flask import abort, jsonify, request
|
from flask import abort, jsonify, request
|
||||||
|
|
||||||
|
from stacosys.db import dao
|
||||||
from stacosys.interface import app
|
from stacosys.interface import app
|
||||||
from stacosys.model.comment import Comment
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/ping", methods=["GET"])
|
@app.route("/ping", methods=["GET"])
|
||||||
def ping():
|
def ping():
|
||||||
return "OK"
|
return "OK"
|
||||||
|
@ -23,12 +24,8 @@ def query_comments():
|
||||||
abort(401)
|
abort(401)
|
||||||
url = request.args.get("url", "")
|
url = request.args.get("url", "")
|
||||||
|
|
||||||
logger.info("retrieve comments for url %s" % (url))
|
logger.info("retrieve comments for url %s" % url)
|
||||||
for comment in (
|
for comment in dao.find_published_comments_by_url(url):
|
||||||
Comment.select(Comment)
|
|
||||||
.where((Comment.url == url) & (Comment.published.is_null(False)))
|
|
||||||
.order_by(+Comment.published)
|
|
||||||
):
|
|
||||||
d = {
|
d = {
|
||||||
"author": comment.author_name,
|
"author": comment.author_name,
|
||||||
"content": comment.content,
|
"content": comment.content,
|
||||||
|
@ -48,12 +45,4 @@ def get_comments_count():
|
||||||
if token != app.config.get("SITE_TOKEN"):
|
if token != app.config.get("SITE_TOKEN"):
|
||||||
abort(401)
|
abort(401)
|
||||||
url = request.args.get("url", "")
|
url = request.args.get("url", "")
|
||||||
if url:
|
return jsonify({"count": dao.count_published_comments(url)})
|
||||||
count = (
|
|
||||||
Comment.select(Comment)
|
|
||||||
.where((Comment.url == url) & (Comment.published.is_null(False)))
|
|
||||||
.count()
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
count = Comment.select(Comment).where(Comment.publishd.is_null(False)).count()
|
|
||||||
return jsonify({"count": count})
|
|
||||||
|
|
|
@ -2,18 +2,17 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
|
||||||
from flask import abort, redirect, request
|
from flask import abort, redirect, request
|
||||||
|
|
||||||
|
from stacosys.db import dao
|
||||||
from stacosys.interface import app
|
from stacosys.interface import app
|
||||||
from stacosys.model.comment import Comment
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/newcomment", methods=["POST"])
|
@app.route("/newcomment", methods=["POST"])
|
||||||
def new_form_comment():
|
def new_form_comment():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = request.form
|
data = request.form
|
||||||
logger.info("form data " + str(data))
|
logger.info("form data " + str(data))
|
||||||
|
@ -26,7 +25,7 @@ def new_form_comment():
|
||||||
# honeypot for spammers
|
# honeypot for spammers
|
||||||
captcha = data.get("remarque", "")
|
captcha = data.get("remarque", "")
|
||||||
if captcha:
|
if captcha:
|
||||||
logger.warn("discard spam: data %s" % data)
|
logger.warning("discard spam: data %s" % data)
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
url = data.get("url", "")
|
url = data.get("url", "")
|
||||||
|
@ -39,23 +38,14 @@ def new_form_comment():
|
||||||
|
|
||||||
# anti-spam again
|
# anti-spam again
|
||||||
if not url or not author_name or not message:
|
if not url or not author_name or not message:
|
||||||
logger.warn("empty field: data %s" % data)
|
logger.warning("empty field: data %s" % data)
|
||||||
|
abort(400)
|
||||||
|
if not check_form_data(data.to_dict()):
|
||||||
|
logger.warning("additional field: data %s" % data)
|
||||||
abort(400)
|
abort(400)
|
||||||
check_form_data(data)
|
|
||||||
|
|
||||||
# add a row to Comment table
|
# add a row to Comment table
|
||||||
created = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
dao.create_comment(url, author_name, author_site, author_gravatar, message)
|
||||||
comment = Comment(
|
|
||||||
url=url,
|
|
||||||
author_name=author_name,
|
|
||||||
author_site=author_site,
|
|
||||||
author_gravatar=author_gravatar,
|
|
||||||
content=message,
|
|
||||||
created=created,
|
|
||||||
notified=None,
|
|
||||||
published=None,
|
|
||||||
)
|
|
||||||
comment.save()
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("new comment failure")
|
logger.exception("new comment failure")
|
||||||
|
@ -64,12 +54,13 @@ def new_form_comment():
|
||||||
return redirect("/redirect/", code=302)
|
return redirect("/redirect/", code=302)
|
||||||
|
|
||||||
|
|
||||||
def check_form_data(data):
|
def check_form_data(d):
|
||||||
fields = ["url", "message", "site", "remarque", "author", "token", "email"]
|
fields = ["url", "message", "site", "remarque", "author", "token", "email"]
|
||||||
d = data.to_dict()
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if field in d:
|
if field in d:
|
||||||
del d[field]
|
del d[field]
|
||||||
if d:
|
|
||||||
logger.warn("additional field: data %s" % data)
|
# filtered = dict(filter(lambda x: x[0] not in fields, data.to_dict().items()))
|
||||||
abort(400)
|
return not d
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
# -*- coding: UTF-8 -*-
|
# -*- coding: UTF-8 -*-
|
||||||
|
|
||||||
from peewee import CharField
|
from peewee import CharField
|
||||||
from peewee import TextField
|
|
||||||
from peewee import DateTimeField
|
from peewee import DateTimeField
|
||||||
from datetime import datetime
|
from peewee import TextField
|
||||||
|
|
||||||
from stacosys.db.database import BaseModel
|
from stacosys.db.database import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue