Compare commits

..

No commits in common. "main" and "v1.0.0" have entirely different histories.
main ... v1.0.0

18 changed files with 161 additions and 1025 deletions

View File

@ -3,6 +3,3 @@ all:
installer: installer:
pynsist src/installer.cfg pynsist src/installer.cfg
@PHONY: black
black:
black src

25
Pipfile
View File

@ -1,25 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
beautifulsoup4 = "*"
coloredlogs = "*"
flask = "*"
tabulate = "*"
colorama = "*"
[dev-packages]
black = "*"
debugpy = "*"
flake8 = "*"
pytest = "*"
pytest-cov = "*"
pytest-mock = "*"
pydocstyle = "*"
pylint = "*"
pynsist = "*"
[requires]
python_version = "3.11"

715
Pipfile.lock generated
View File

@ -1,715 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "7c711e876affdb7a45715a84641460c07f98a71fd83bb8eb6cbf4a4b13e9ab9a"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.11"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"astroid": {
"hashes": [
"sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819",
"sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.0'",
"version": "==3.1.0"
},
"attrs": {
"hashes": [
"sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30",
"sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==23.2.0"
},
"beautifulsoup4": {
"hashes": [
"sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051",
"sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"
],
"index": "pypi",
"markers": "python_full_version >= '3.6.0'",
"version": "==4.12.3"
},
"black": {
"hashes": [
"sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8",
"sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8",
"sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd",
"sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9",
"sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31",
"sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92",
"sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f",
"sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29",
"sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4",
"sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693",
"sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218",
"sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a",
"sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23",
"sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0",
"sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982",
"sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894",
"sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540",
"sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430",
"sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b",
"sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2",
"sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6",
"sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==24.2.0"
},
"blinker": {
"hashes": [
"sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9",
"sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.7.0"
},
"certifi": {
"hashes": [
"sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f",
"sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==2024.2.2"
},
"charset-normalizer": {
"hashes": [
"sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027",
"sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087",
"sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786",
"sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8",
"sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09",
"sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185",
"sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574",
"sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e",
"sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519",
"sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898",
"sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269",
"sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3",
"sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f",
"sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6",
"sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8",
"sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a",
"sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73",
"sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc",
"sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714",
"sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2",
"sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc",
"sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce",
"sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d",
"sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e",
"sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6",
"sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269",
"sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96",
"sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d",
"sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a",
"sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4",
"sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77",
"sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d",
"sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0",
"sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed",
"sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068",
"sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac",
"sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25",
"sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8",
"sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab",
"sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26",
"sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2",
"sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db",
"sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f",
"sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5",
"sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99",
"sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c",
"sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d",
"sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811",
"sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa",
"sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a",
"sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03",
"sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b",
"sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04",
"sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c",
"sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001",
"sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458",
"sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389",
"sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99",
"sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985",
"sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537",
"sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238",
"sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f",
"sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d",
"sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796",
"sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a",
"sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143",
"sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8",
"sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c",
"sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5",
"sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5",
"sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711",
"sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4",
"sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6",
"sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c",
"sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7",
"sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4",
"sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b",
"sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae",
"sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12",
"sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c",
"sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae",
"sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8",
"sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887",
"sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b",
"sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4",
"sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f",
"sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5",
"sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33",
"sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519",
"sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"
],
"index": "pypi",
"markers": "python_full_version >= '3.7.0'",
"version": "==3.3.2"
},
"click": {
"hashes": [
"sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
"sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==8.1.7"
},
"colorama": {
"hashes": [
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
],
"index": "pypi",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
"version": "==0.4.6"
},
"coloredlogs": {
"hashes": [
"sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934",
"sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"
],
"index": "pypi",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==15.0.1"
},
"coverage": {
"extras": [
"toml"
],
"hashes": [
"sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa",
"sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003",
"sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f",
"sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c",
"sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e",
"sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0",
"sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9",
"sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52",
"sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e",
"sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454",
"sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0",
"sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079",
"sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352",
"sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f",
"sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30",
"sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe",
"sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113",
"sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765",
"sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc",
"sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e",
"sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501",
"sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7",
"sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2",
"sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f",
"sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4",
"sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524",
"sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c",
"sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51",
"sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840",
"sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6",
"sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee",
"sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e",
"sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45",
"sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba",
"sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d",
"sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3",
"sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10",
"sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e",
"sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb",
"sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9",
"sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a",
"sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47",
"sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1",
"sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3",
"sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914",
"sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328",
"sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6",
"sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d",
"sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0",
"sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94",
"sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc",
"sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==7.4.3"
},
"debugpy": {
"hashes": [
"sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb",
"sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146",
"sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8",
"sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242",
"sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0",
"sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741",
"sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539",
"sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23",
"sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3",
"sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39",
"sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd",
"sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9",
"sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace",
"sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42",
"sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0",
"sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7",
"sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e",
"sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234",
"sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98",
"sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703",
"sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42",
"sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.8.1"
},
"dill": {
"hashes": [
"sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca",
"sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==0.3.8"
},
"distlib": {
"hashes": [
"sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784",
"sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"
],
"index": "pypi",
"version": "==0.3.8"
},
"exceptiongroup": {
"hashes": [
"sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14",
"sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"flake8": {
"hashes": [
"sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132",
"sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.1'",
"version": "==7.0.0"
},
"flask": {
"hashes": [
"sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e",
"sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==3.0.2"
},
"humanfriendly": {
"hashes": [
"sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477",
"sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"
],
"index": "pypi",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==10.0"
},
"idna": {
"hashes": [
"sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca",
"sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"
],
"index": "pypi",
"markers": "python_version >= '3.5'",
"version": "==3.6"
},
"iniconfig": {
"hashes": [
"sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
"sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.0.0"
},
"isort": {
"hashes": [
"sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109",
"sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.0'",
"version": "==5.13.2"
},
"itsdangerous": {
"hashes": [
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.1.2"
},
"jinja2": {
"hashes": [
"sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
"sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==3.1.3"
},
"markupsafe": {
"hashes": [
"sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf",
"sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff",
"sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f",
"sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3",
"sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532",
"sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f",
"sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617",
"sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df",
"sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4",
"sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906",
"sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f",
"sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4",
"sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8",
"sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371",
"sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2",
"sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465",
"sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52",
"sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6",
"sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169",
"sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad",
"sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2",
"sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0",
"sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029",
"sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f",
"sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a",
"sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced",
"sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5",
"sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c",
"sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf",
"sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9",
"sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb",
"sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad",
"sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3",
"sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1",
"sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46",
"sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc",
"sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a",
"sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee",
"sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900",
"sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5",
"sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea",
"sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f",
"sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5",
"sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e",
"sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a",
"sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f",
"sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50",
"sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a",
"sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b",
"sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4",
"sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff",
"sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2",
"sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46",
"sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b",
"sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf",
"sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5",
"sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5",
"sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab",
"sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd",
"sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.1.5"
},
"mccabe": {
"hashes": [
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==0.7.0"
},
"mypy-extensions": {
"hashes": [
"sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
"sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
],
"index": "pypi",
"markers": "python_version >= '3.5'",
"version": "==1.0.0"
},
"packaging": {
"hashes": [
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==23.2"
},
"pathspec": {
"hashes": [
"sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08",
"sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==0.12.1"
},
"platformdirs": {
"hashes": [
"sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068",
"sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==4.2.0"
},
"pluggy": {
"hashes": [
"sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981",
"sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.4.0"
},
"pycodestyle": {
"hashes": [
"sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f",
"sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==2.11.1"
},
"pydocstyle": {
"hashes": [
"sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019",
"sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==6.3.0"
},
"pyflakes": {
"hashes": [
"sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f",
"sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==3.2.0"
},
"pylint": {
"hashes": [
"sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74",
"sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.0'",
"version": "==3.1.0"
},
"pynsist": {
"hashes": [
"sha256:465c4596cba5cc3698d4719ddc7afea31ce9dc8936a7b4d3feffec6e8adc0b5d",
"sha256:7d3e8343c10cdbfb262ab63201a62d38ed86f4f2d0cffc2677c9917793d800a6"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==2.8"
},
"pyparsing": {
"hashes": [
"sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad",
"sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"
],
"index": "pypi",
"markers": "python_full_version >= '3.6.8'",
"version": "==3.1.2"
},
"pytest": {
"hashes": [
"sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd",
"sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==8.0.2"
},
"pytest-cov": {
"hashes": [
"sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6",
"sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==4.1.0"
},
"pytest-mock": {
"hashes": [
"sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f",
"sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==3.12.0"
},
"requests": {
"hashes": [
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.31.0"
},
"requests-download": {
"hashes": [
"sha256:92d895a6ca51ea51aa42bab864bddaee31b5601c7e7e1ade4c27b0eb6695d846",
"sha256:994d9d332befae6616f562769bab163f08d6404dc7e28fb7bfed4a0a43a754ad"
],
"index": "pypi",
"version": "==0.1.2"
},
"snowballstemmer": {
"hashes": [
"sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1",
"sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"
],
"index": "pypi",
"version": "==2.2.0"
},
"soupsieve": {
"hashes": [
"sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690",
"sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==2.5"
},
"tabulate": {
"hashes": [
"sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c",
"sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==0.9.0"
},
"toml": {
"hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"index": "pypi",
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"tomli": {
"hashes": [
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.0.1"
},
"tomlkit": {
"hashes": [
"sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b",
"sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==0.12.4"
},
"urllib3": {
"hashes": [
"sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d",
"sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==2.2.1"
},
"vulture": {
"hashes": [
"sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba",
"sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==2.11"
},
"werkzeug": {
"hashes": [
"sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc",
"sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==3.0.1"
},
"yarg": {
"hashes": [
"sha256:4f9cebdc00fac946c9bf2783d634e538a71c7d280a4d806d45fd4dc0ef441492",
"sha256:55695bf4d1e3e7f756496c36a69ba32c40d18f821e38f61d028f6049e5e15911"
],
"index": "pypi",
"version": "==0.1.9"
}
},
"develop": {}
}

View File

@ -1,51 +1,41 @@
astroid==3.1.0 attrs==22.1.0
attrs==23.2.0 beautifulsoup4==4.11.1
beautifulsoup4==4.12.3 black==23.11.0
black==24.2.0 blinker==1.6.2
blinker==1.7.0 certifi==2023.7.22
certifi==2024.2.2 charset-normalizer==3.2.0
charset-normalizer==3.3.2
click==8.1.7 click==8.1.7
colorama==0.4.6 colorama==0.4.6
coloredlogs==15.0.1 coloredlogs==15.0.1
coverage==7.4.3 coverage==6.5.0
debugpy==1.8.1 debugpy==1.6.7
dill==0.3.8 distlib==0.3.7
distlib==0.3.8 exceptiongroup==1.0.1
exceptiongroup==1.2.0 Flask==2.3.3
flake8==7.0.0
Flask==3.0.2
humanfriendly==10.0 humanfriendly==10.0
idna==3.6 idna==3.4
iniconfig==2.0.0 iniconfig==1.1.1
isort==5.13.2
itsdangerous==2.1.2 itsdangerous==2.1.2
Jinja2==3.1.3 Jinja2==3.1.2
MarkupSafe==2.1.5 MarkupSafe==2.1.3
mccabe==0.7.0
mypy-extensions==1.0.0 mypy-extensions==1.0.0
packaging==23.2 packaging==23.2
pathspec==0.12.1 pathspec==0.11.2
platformdirs==4.2.0 platformdirs==4.0.0
pluggy==1.4.0 pluggy==1.0.0
pycodestyle==2.11.1
pydocstyle==6.3.0 pydocstyle==6.3.0
pyflakes==3.2.0 pyflakes==3.1.0
pylint==3.1.0
pynsist==2.8 pynsist==2.8
pyparsing==3.1.2 pyparsing==3.0.9
pytest==8.0.2 pytest==7.2.0
pytest-cov==4.1.0 pytest-cov==4.0.0
pytest-mock==3.12.0 pytest-mock==3.10.0
requests==2.31.0 requests==2.31.0
requests_download==0.1.2 requests_download==0.1.2
snowballstemmer==2.2.0 snowballstemmer==2.2.0
soupsieve==2.5 soupsieve==2.3.2.post1
tabulate==0.9.0 tabulate==0.9.0
toml==0.10.2
tomli==2.0.1 tomli==2.0.1
tomlkit==0.12.4 urllib3==2.0.5
urllib3==2.2.1 Werkzeug==2.3.7
vulture==2.11
Werkzeug==3.0.1
yarg==0.1.9 yarg==0.1.9

View File

@ -1,6 +1,6 @@
[Application] [Application]
name=Solo Auswertung name=Solo Auswertung
version=2.1.1 version=1.0.0
# How to launch the app - this calls the 'main' function from the 'myapp' package: # How to launch the app - this calls the 'main' function from the 'myapp' package:
entry_point=main:main entry_point=main:main
# icon=myapp.ico # icon=myapp.ico
@ -16,19 +16,40 @@ console=true
[Include] [Include]
# Packages from PyPI that your application requires, one per line # Packages from PyPI that your application requires, one per line
# These must have wheels on PyPI: # These must have wheels on PyPI:
pypi_wheels = beautifulsoup4==4.12.3 pypi_wheels = attrs==22.1.0
blinker==1.8.2 beautifulsoup4==4.11.1
blinker==1.6.2
certifi==2023.7.22
charset-normalizer==3.2.0
click==8.1.7 click==8.1.7
colorama==0.4.6 colorama==0.4.6
coloredlogs==15.0.1 coloredlogs==15.0.1
flask==3.0.3 coverage==6.5.0
debugpy==1.6.7
distlib==0.3.7
exceptiongroup==1.0.1
Flask==2.3.3
humanfriendly==10.0 humanfriendly==10.0
itsdangerous==2.2.0 idna==3.4
jinja2==3.1.4 iniconfig==1.1.1
markupsafe==2.1.5 itsdangerous==2.1.2
soupsieve==2.5 Jinja2==3.1.2
MarkupSafe==2.1.3
packaging==21.3
pluggy==1.0.0
pynsist==2.8
pyparsing==3.0.9
pytest==7.2.0
pytest-cov==4.0.0
pytest-mock==3.10.0
requests==2.31.0
requests_download==0.1.2
soupsieve==2.3.2.post1
tabulate==0.9.0 tabulate==0.9.0
werkzeug==3.0.3 tomli==2.0.1
urllib3==2.0.5
Werkzeug==2.3.7
yarg==0.1.9
packages = solo_turnier packages = solo_turnier

View File

@ -1,6 +1,8 @@
import argparse import argparse
import logging import logging
import debugpy
class Cli: class Cli:
def __init__(self, l: logging.Logger): def __init__(self, l: logging.Logger):
@ -48,8 +50,6 @@ class Cli:
self.__args = parser.parse_args() self.__args = parser.parse_args()
if self.__args.debug: if self.__args.debug:
import debugpy
debugpy.listen(5678) debugpy.listen(5678)
debugpy.wait_for_client() debugpy.wait_for_client()

View File

@ -36,34 +36,43 @@ class CombinedCompetitionClass:
Class_t = CompetitionClass | CombinedCompetitionClass Class_t = CompetitionClass | CombinedCompetitionClass
class NoEClassException(Exception):
def __init__(self, *args):
super(NoEClassException, self).__init__(*args)
class CompetitionClassParser: class CompetitionClassParser:
E = CompetitionClass("E") NEWC = CompetitionClass("Newc.")
BEG = CompetitionClass("Beg.")
ADV = CompetitionClass("Adv.")
PREVIEW = CompetitionClass("Sichtung")
def __init__(self): def __init__(self):
self.mapNames = { self.mapNames = {
"E": self.E, "Newc": self.NEWC,
"Newc.": self.NEWC,
"Newcomer": self.NEWC,
"Beg": self.BEG,
"Beg.": self.BEG,
"Beginner": self.BEG,
"Adv": self.ADV,
"Adv.": self.ADV,
"Advanced": self.ADV,
} }
self.namesPreview = ["Sichtung"] self.namesPreview = ["Sichtung"]
self.mapShortNames = self.mapNames self.mapShortNames = {
"N": self.NEWC,
"B": self.BEG,
"A": self.ADV,
}
def parseClass(self, cls: str, allowPreview: bool = False) -> Class_t: def parseClass(self, cls: str, allowPreview: bool = False) -> Class_t:
# match = re.compile("^(\\w+\\.?)/(\\w+\\.?)$").match(cls) if allowPreview and cls in self.namesPreview:
# if match is not None: return self.PREVIEW
# clsA = self.mapNames[match.group(1)]
# clsB = self.mapNames[match.group(2)]
# return CombinedCompetitionClass(clsA, clsB)
# else:
# return self.mapNames[cls]
if cls in self.mapNames: match = re.compile("^(\\w+\\.?)/(\\w+\\.?)$").match(cls)
return self.mapNames[cls] if match is not None:
clsA = self.mapNames[match.group(1)]
clsB = self.mapNames[match.group(2)]
return CombinedCompetitionClass(clsA, clsB)
else: else:
raise NoEClassException(f'The class "{cls}" is not parsable.') return self.mapNames[cls]
def parseAbbreviatedClass(self, cls: str) -> Class_t: def parseAbbreviatedClass(self, cls: str) -> Class_t:
return self.mapShortNames[cls] return self.mapShortNames[cls]

View File

@ -15,11 +15,6 @@ class IncompleteRoundException(Exception):
super(IncompleteRoundException, self).__init__(*args) super(IncompleteRoundException, self).__init__(*args)
class CannotParseRowException(Exception):
def __init__(self, *args):
super(CannotParseRowException, self).__init__(*args)
class HtmlParser: class HtmlParser:
def __init__(self, text: str, fileName: str = None): def __init__(self, text: str, fileName: str = None):
self.l = logging.getLogger("solo_turnier.html_parser") self.l = logging.getLogger("solo_turnier.html_parser")
@ -41,19 +36,10 @@ class HtmlParser:
if title is None: if title is None:
title = self.getEventTitle() title = self.getEventTitle()
match = re.compile('.*?OT, Solos (.*?)(?: ".*")?').fullmatch(title)
if match is None:
self.l.debug(
'Parsing HTML page title "%s" as OT failed. Falling back to legacy ETW.',
title,
)
match = re.compile('.*?ETW, Solos (.*?)(?: ".*")?').fullmatch(title) match = re.compile('.*?ETW, Solos (.*?)(?: ".*")?').fullmatch(title)
if match is None: if match is None:
self.l.info( self.l.error('Cannot parse html title "%s". Possible bug?', title)
'Cannot parse html title "%s". Is it a solo competition? Possible bug.', raise Exception(f'Cannot parse title "{title}"')
title,
)
raise Exception(f'Cannot parse title "{title}".')
rest = match.group(1) rest = match.group(1)
rawGroup, rawClass, dance = rest.split(" ", 2) rawGroup, rawClass, dance = rest.split(" ", 2)
@ -67,144 +53,46 @@ class HtmlParser:
def parseResult(self) -> HtmlResultImport: def parseResult(self) -> HtmlResultImport:
participants = {} participants = {}
nameRegex = re.compile("(.*) \\(([0-9]+)\\)") def __parseRows(rows, finalist: bool):
def __parseRow(row):
tds = row.find_all("td")
def __parseNameAndId(string: str, tds) -> tuple[str, str]: if len(tds) != 2:
match = nameRegex.fullmatch(string)
if match is None:
self.l.error("Could not match %s to regex search pattern", str(tds))
raise CannotParseRowException(
f"Could not match {tds} to regex search pattern for 'name (id)'"
)
name = match.group(1)
number = match.group(2)
return name, number
def __parseRows(rows, parsers):
def parseRow(row):
for parser in parsers:
try:
parser(row("td"))
return return
except CannotParseRowException:
pass
# No parser was found if we get here. if tds[1].contents[0].startswith("Alle Starter weiter genommen."):
self.l.error("Cannot parse row in table.") self.l.info("No excluded starters found.")
return
for row in rows: regex = re.compile("(.*) \\(([0-9]+)\\)")
parseRow(row)
def __ensureLength(tds, length):
if len(tds) != length:
raise CannotParseRowException(
"The row has %d entries but %d are expected." % (len(tds), length)
)
def __parseFormationRowGeneric(tds, finalist):
__ensureLength(tds, 2)
place = tds[0].contents[0] place = tds[0].contents[0]
name, number = __parseNameAndId(tds[1].contents[0], tds)
match = regex.fullmatch(tds[1].contents[0])
if match is None:
self.l.error("Could not match %s to regex search pattern", str(tds))
raise Exception(f"Could not match {tds} to regex search pattern")
name = match.group(1)
number = match.group(2)
participant = HtmlParticipant(name, number) participant = HtmlParticipant(name, number)
participant.finalist = finalist participant.finalist = finalist
participant.club = ""
participants[participant] = place participants[participant] = place
for row in rows:
__parseRow(row)
def __parseFirstTable(table): def __parseFirstTable(table):
roundName = table.tr.td.contents[0] roundName = table.tr.td.contents[0]
if roundName != "Endrunde": if roundName != "Endrunde":
self.l.warning("Found table with round name %s.", roundName) self.l.warning("Found table with round name %s.", roundName)
raise IncompleteRoundException("Could not parse HTML file") raise IncompleteRoundException("Could not parse HTML file")
def __parseFormationRow(tds): __parseRows(table.find_all("tr")[2:], True)
__parseFormationRowGeneric(tds, True)
def __parsePairRow(tds):
__ensureLength(tds, 4)
place = tds[0].contents[0]
tdNameClub = tds[1]
tdClub = tdNameClub.i.extract()
name, number = __parseNameAndId(tdNameClub.contents[0], tds)
participant = HtmlParticipant(name, number)
participant.finalist = True
participant.club = tdClub.contents[0]
participants[participant] = place
__parseRows(
table.find_all("tr")[2:],
[
__parsePairRow,
__parseFormationRow,
],
)
def __parseRemainingTables(tables): def __parseRemainingTables(tables):
def __parseFormationRow(tds):
__parseFormationRowGeneric(tds, False)
def __parsePairRow(tds):
__ensureLength(tds, 3)
place = tds[0].contents[0]
name, number = __parseNameAndId(tds[1].contents[0], tds)
participant = HtmlParticipant(name, number)
participant.finalist = False
participant.club = tds[2].contents[0]
participants[participant] = place
def __parseSeparatorRow(tds):
__ensureLength(tds, 1)
if len(list(tds[0].stripped_strings)) == 0:
return
raise CannotParseRowException("No empty string")
regexZwischenRunde = re.compile("[1-9]\. Zwischenrunde")
def __parseRoundHeading(tds):
__ensureLength(tds, 1)
s = "".join(tds[0].stripped_strings)
if s.startswith("Vorrunde"):
return
if regexZwischenRunde.match(s) is not None:
return
raise CannotParseRowException("Kein Header einer Runde gefunden.")
def __parseAllSolosQualifiedFormation(tds):
__ensureLength(tds, 2)
if tds[1].contents[0].startswith("Alle Starter weiter genommen."):
return
raise CannotParseRowException(
'Not found the text "Alle Starter weiter genommen"'
)
def __parseAllSolosQualifiedPair(tds):
__ensureLength(tds, 3)
if tds[1].contents[0].startswith("Alle Mannschaften weiter genommen."):
return
raise CannotParseRowException(
'Not found the text "Alle Mannschaften weiter genommen"'
)
for table in tables: for table in tables:
__parseRows( __parseRows(table.find_all("tr"), False)
table.find_all("tr"),
[
__parseAllSolosQualifiedFormation,
__parseAllSolosQualifiedPair,
__parsePairRow,
__parseFormationRow,
__parseSeparatorRow,
__parseRoundHeading,
],
)
tables = self.soup.find("div", class_="extract").find_all("table") tables = self.soup.find("div", class_="extract").find_all("table")

View File

@ -116,27 +116,17 @@ class ConsoleOutputter(AbstractOutputter):
placeNative = str(result.nativePlace) placeNative = str(result.nativePlace)
place = str(result.place) place = str(result.place)
lineOne = f"{placeNative}" lineOne = f"{placeNative} ({result.nativeClass})"
lineTwo = f"[{place} in {result.competitionClass}]"
lines = [lineOne]
groupCompetition = result.competitionGroup
if isinstance(groupCompetition, solo_turnier.group.CombinedGroup):
lineTwo = f"[{place} in {groupCompetition}]"
lines.append(lineTwo)
lines = [lineOne, lineTwo]
if not result.finalist: if not result.finalist:
lines = ["kein/e Finalist/in"] + lines lines = ["kein/e Finalist/in"] + lines
return "\n".join(lines) return "\n".join(lines)
mappedResults = map(mapResultColumn, results) mappedResults = map(mapResultColumn, results)
tableRow = [f"{participant.name} ({participant.id})"] + list(mappedResults)
participantName = f"{participant.name} ({participant.id})"
if participant.club is not None:
participantName = f"{participantName}, {participant.club}"
tableRow = [f"{participantName}"] + list(mappedResults)
tableData.append(tableRow) tableData.append(tableRow)
self.l.log(5, "table data: %s", pprint.pformat(tableData)) self.l.log(5, "table data: %s", pprint.pformat(tableData))

View File

@ -1,3 +1,4 @@
.tab-summary { .tab-summary {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
@ -41,7 +42,8 @@
.section, .section,
.section table tr, .section table tr,
.section table td { .section table td
{
page-break-inside: avoid; page-break-inside: avoid;
} }

View File

@ -34,12 +34,7 @@
{% endif %} {% endif %}
{% if participant.finalist or not onlyFinalists %} {% if participant.finalist or not onlyFinalists %}
<tr class="{{ rowCls }}"> <tr class="{{ rowCls }}">
<td> <td>{{ participant.name }} ({{ participant.id }})</td>
{{ participant.name }} ({{ participant.id }})
{% if participant.club is not none %}
, {{ participant.club}}
{% endif %}
</td>
{% for dance in data.resultsPerGroup[group].dances %} {% for dance in data.resultsPerGroup[group].dances %}
{% block danceResult scoped %} {% block danceResult scoped %}
{% set res = activeGroup[participant][loop.index0] %} {% set res = activeGroup[participant][loop.index0] %}
@ -49,11 +44,10 @@
Kein/e Finalist/in <br /> Kein/e Finalist/in <br />
{% endif %} {% endif %}
<span class="{% if not res.finalist %}no-finalist-dance{% endif %}"> <span class="{% if not res.finalist %}no-finalist-dance{% endif %}">
{{ res.getNativePlace() }} {{ res.getNativePlace() }} ({{ res.nativeClass }}) <br />
{% if res.isCombinedGroup() %} </span>
<br /> <span class="competition-place">
({{ res.place }} {{ res.competitionGroup }}) {{ res.place }} in {{ res.competitionClass }}
{% endif %}
</span> </span>
{% endif %} {% endif %}
</td> </td>

View File

@ -26,7 +26,9 @@ class HtmlCompetitionTotalResults:
) -> list[HtmlSingleCompetitionResult]: ) -> list[HtmlSingleCompetitionResult]:
return self.results[self.__getTuple(group, class_, dance, id)] return self.results[self.__getTuple(group, class_, dance, id)]
def getById(self, id: int) -> dict[ def getById(
self, id: int
) -> dict[
tuple[str, solo_turnier.group.Group_t, solo_turnier.competition_class.Class_t], tuple[str, solo_turnier.group.Group_t, solo_turnier.competition_class.Class_t],
HtmlSingleCompetitionResult, HtmlSingleCompetitionResult,
]: ]:

View File

@ -3,7 +3,6 @@ class HtmlParticipant:
self.name = name self.name = name
self.id = id self.id = id
self.finalist = None self.finalist = None
self.club = None
def __eq__(self, o): def __eq__(self, o):
if type(o) != HtmlParticipant: if type(o) != HtmlParticipant:

View File

@ -2,11 +2,10 @@ from .place import Place
class HtmlSingleCompetitionResult: class HtmlSingleCompetitionResult:
def __init__(self, name: str, place: Place, finalist: bool, club: str): def __init__(self, name: str, place: Place, finalist: bool):
self.name = name self.name = name
self.place = place self.place = place
self.finalist = finalist self.finalist = finalist
self.club = club
def __repr__(self): def __repr__(self):
place = self.place place = self.place

View File

@ -9,12 +9,10 @@ class Participant(Person):
name: str, name: str,
id: int, id: int,
finalist: bool = None, finalist: bool = None,
club: str = None,
): ):
super().__init__(name) super().__init__(name)
self.id = id self.id = id
self.finalist = finalist self.finalist = finalist
self.club = club
def __repr__(self): def __repr__(self):
if self.finalist == True: if self.finalist == True:

View File

@ -7,7 +7,6 @@ class SingleParticipantResult:
def __init__( def __init__(
self, self,
competitionClass: solo_turnier.competition_class.Class_t, competitionClass: solo_turnier.competition_class.Class_t,
competitionGroup: solo_turnier.group.Group_t,
nativeClass: solo_turnier.competition_class.CompetitionClass, nativeClass: solo_turnier.competition_class.CompetitionClass,
dance: str, dance: str,
finalist: bool, finalist: bool,
@ -15,7 +14,6 @@ class SingleParticipantResult:
nativePlace: Place = None, nativePlace: Place = None,
): ):
self.competitionClass = competitionClass self.competitionClass = competitionClass
self.competitionGroup = competitionGroup
self.nativeClass = nativeClass self.nativeClass = nativeClass
self.dance = dance self.dance = dance
self.finalist = finalist self.finalist = finalist
@ -29,6 +27,3 @@ class SingleParticipantResult:
def getNativePlace(self) -> str: def getNativePlace(self) -> str:
return str(self.nativePlace) return str(self.nativePlace)
def isCombinedGroup(self) -> bool:
return isinstance(self.competitionGroup, solo_turnier.group.CombinedGroup)

View File

@ -9,7 +9,7 @@ ParserList_t = dict[str, html_parser.HtmlParser]
class ResultExtractor: class ResultExtractor:
def __init__(self): def __init__(self):
self.l = logging.getLogger(__name__) self.l = logging.getLogger("solo_turnier.worker.ResultExtractor")
self.rePlaceSingle = re.compile(" *([0-9]+) *") self.rePlaceSingle = re.compile(" *([0-9]+) *")
self.rePlaceDouble = re.compile(" *([0-9]+) *- *([0-9]+) *") self.rePlaceDouble = re.compile(" *([0-9]+) *- *([0-9]+) *")
@ -31,15 +31,8 @@ class ResultExtractor:
try: try:
data = parser.guessDataFromHtmlTitle() data = parser.guessDataFromHtmlTitle()
except competition_class.NoEClassException as ex:
self.l.info(
"The HTML file %s does not represent a solo E class. Skipping it. (%s)",
filePair[0],
ex,
)
continue
except: except:
self.l.warning( self.l.error(
"Cannot parse HTML file %s to check if it is a valid result. Check manually.", "Cannot parse HTML file %s to check if it is a valid result. Check manually.",
filePair[0], filePair[0],
) )
@ -85,7 +78,7 @@ class ResultExtractor:
placeStr = result.results[person] placeStr = result.results[person]
place = self._extractPlace(placeStr) place = self._extractPlace(placeStr)
competitionResult = types.HtmlSingleCompetitionResult( competitionResult = types.HtmlSingleCompetitionResult(
person.name, place, person.finalist, person.club person.name, place, person.finalist
) )
results.add( results.add(
competitionGroup, competitionGroup,

View File

@ -9,7 +9,7 @@ from .. import competition_class
class Worker: class Worker:
def __init__(self): def __init__(self):
self.l = logging.getLogger(__name__) self.l = logging.getLogger("solo_turnier.worker.Worker")
self._allDances = ["Samba", "Cha Cha", "Rumba", "Paso Doble", "Jive"] + [ self._allDances = ["Samba", "Cha Cha", "Rumba", "Paso Doble", "Jive"] + [
"Langs. Walzer", "Langs. Walzer",
"Tango", "Tango",
@ -42,10 +42,10 @@ class Worker:
self.l.debug("Found groups in the dataset: %s", groups) self.l.debug("Found groups in the dataset: %s", groups)
invertedGroupMapping = self._invertGroupMapping(groupMapping, groups) invertedGroupMapping = self._invertGroupMapping(groupMapping, groups)
self.l.log(5, "Inverted group mapping: %s", invertedGroupMapping) self.l.log(5, "Inverted group maping: %s", invertedGroupMapping)
idToParticipantMapping = self._invertIdMapping(importedData.htmlResults) idToParticipantMapping = self._invertIdMapping(importedData.htmlResults)
self.l.log(5, "Id to participant mapping: %s", idToParticipantMapping) self.l.log(5, "Id to participant mappting: %s", idToParticipantMapping)
totalResult = {} totalResult = {}
@ -96,7 +96,6 @@ class Worker:
singleResult = solo_turnier.types.SingleParticipantResult( singleResult = solo_turnier.types.SingleParticipantResult(
competitionClass=tup.class_, competitionClass=tup.class_,
nativeClass=tup.class_, nativeClass=tup.class_,
competitionGroup=tup.group,
dance=tup.dance, dance=tup.dance,
finalist=singleHtmlResult.finalist, finalist=singleHtmlResult.finalist,
place=singleHtmlResult.place, place=singleHtmlResult.place,
@ -330,7 +329,7 @@ class Worker:
if id not in mapping: if id not in mapping:
mapping[id] = solo_turnier.types.Participant( mapping[id] = solo_turnier.types.Participant(
name=results[0].name, id=id, club=results[0].club name=results[0].name, id=id
) )
else: else:
if mapping[id].name != results[0].name or mapping[id].id != id: if mapping[id].name != results[0].name or mapping[id].id != id: