Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
wangsen
MinerU
Commits
bcbbee8c
Unverified
Commit
bcbbee8c
authored
Jun 13, 2025
by
Xiaomeng Zhao
Committed by
GitHub
Jun 13, 2025
Browse files
Merge pull request #2622 from myhloli/dev
Dev
parents
3cc3f754
ced5a7b4
Changes
561
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
0 additions
and
1928 deletions
+0
-1928
projects/web_demo/poetry.lock
projects/web_demo/poetry.lock
+0
-687
projects/web_demo/pyproject.toml
projects/web_demo/pyproject.toml
+0
-24
projects/web_demo/requirements.txt
projects/web_demo/requirements.txt
+0
-13
projects/web_demo/tests/__init__.py
projects/web_demo/tests/__init__.py
+0
-0
projects/web_demo/web_demo/__init__.py
projects/web_demo/web_demo/__init__.py
+0
-1
projects/web_demo/web_demo/api/__init__.py
projects/web_demo/web_demo/api/__init__.py
+0
-38
projects/web_demo/web_demo/api/analysis/__init__.py
projects/web_demo/web_demo/api/analysis/__init__.py
+0
-20
projects/web_demo/web_demo/api/analysis/analysis_view.py
projects/web_demo/web_demo/api/analysis/analysis_view.py
+0
-242
projects/web_demo/web_demo/api/analysis/ext.py
projects/web_demo/web_demo/api/analysis/ext.py
+0
-25
projects/web_demo/web_demo/api/analysis/formula_ext.py
projects/web_demo/web_demo/api/analysis/formula_ext.py
+0
-280
projects/web_demo/web_demo/api/analysis/img_md_view.py
projects/web_demo/web_demo/api/analysis/img_md_view.py
+0
-46
projects/web_demo/web_demo/api/analysis/markdown_view.py
projects/web_demo/web_demo/api/analysis/markdown_view.py
+0
-44
projects/web_demo/web_demo/api/analysis/models.py
projects/web_demo/web_demo/api/analysis/models.py
+0
-29
projects/web_demo/web_demo/api/analysis/pdf_ext.py
projects/web_demo/web_demo/api/analysis/pdf_ext.py
+0
-175
projects/web_demo/web_demo/api/analysis/serialization.py
projects/web_demo/web_demo/api/analysis/serialization.py
+0
-28
projects/web_demo/web_demo/api/analysis/task_view.py
projects/web_demo/web_demo/api/analysis/task_view.py
+0
-102
projects/web_demo/web_demo/api/analysis/upload_view.py
projects/web_demo/web_demo/api/analysis/upload_view.py
+0
-89
projects/web_demo/web_demo/api/extentions.py
projects/web_demo/web_demo/api/extentions.py
+0
-63
projects/web_demo/web_demo/api/react_app/__init__.py
projects/web_demo/web_demo/api/react_app/__init__.py
+0
-11
projects/web_demo/web_demo/api/react_app/react_app_view.py
projects/web_demo/web_demo/api/react_app/react_app_view.py
+0
-11
No files found.
projects/web_demo/poetry.lock
deleted
100644 → 0
View file @
3cc3f754
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "alembic"
version = "1.13.2"
description = "A database migration tool for SQLAlchemy."
optional = false
python-versions = ">=3.8"
files = [
{file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"},
{file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"},
]
[package.dependencies]
Mako = "*"
SQLAlchemy = ">=1.3.0"
typing-extensions = ">=4"
[package.extras]
tz = ["backports.zoneinfo"]
[[package]]
name = "aniso8601"
version = "9.0.1"
description = "A library for parsing ISO 8601 strings."
optional = false
python-versions = "*"
files = [
{file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
{file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
]
[package.extras]
dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
[[package]]
name = "blinker"
version = "1.8.2"
description = "Fast, simple object-to-object and broadcast signaling"
optional = false
python-versions = ">=3.8"
files = [
{file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"},
{file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"},
]
[[package]]
name = "click"
version = "8.1.7"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "flask"
version = "3.0.3"
description = "A simple framework for building complex web applications."
optional = false
python-versions = ">=3.8"
files = [
{file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"},
{file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"},
]
[package.dependencies]
blinker = ">=1.6.2"
click = ">=8.1.3"
itsdangerous = ">=2.1.2"
Jinja2 = ">=3.1.2"
Werkzeug = ">=3.0.0"
[package.extras]
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
[[package]]
name = "flask-cors"
version = "5.0.0"
description = "A Flask extension adding a decorator for CORS support"
optional = false
python-versions = "*"
files = [
{file = "Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc"},
{file = "flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef"},
]
[package.dependencies]
Flask = ">=0.9"
[[package]]
name = "flask-jwt-extended"
version = "4.6.0"
description = "Extended JWT integration with Flask"
optional = false
python-versions = ">=3.7,<4"
files = [
{file = "Flask-JWT-Extended-4.6.0.tar.gz", hash = "sha256:9215d05a9413d3855764bcd67035e75819d23af2fafb6b55197eb5a3313fdfb2"},
{file = "Flask_JWT_Extended-4.6.0-py2.py3-none-any.whl", hash = "sha256:63a28fc9731bcc6c4b8815b6f954b5904caa534fc2ae9b93b1d3ef12930dca95"},
]
[package.dependencies]
Flask = ">=2.0,<4.0"
PyJWT = ">=2.0,<3.0"
Werkzeug = ">=0.14"
[package.extras]
asymmetric-crypto = ["cryptography (>=3.3.1)"]
[[package]]
name = "flask-marshmallow"
version = "1.2.1"
description = "Flask + marshmallow for beautiful APIs"
optional = false
python-versions = ">=3.8"
files = [
{file = "flask_marshmallow-1.2.1-py3-none-any.whl", hash = "sha256:10b5048ecfaa26f7c8d0aed7d81083164450e6be8e81c04b3d4a586b3f7b6678"},
{file = "flask_marshmallow-1.2.1.tar.gz", hash = "sha256:00ee96399ed664963afff3b5d6ee518640b0f91dbc2aace2b5abcf32f40ef23a"},
]
[package.dependencies]
Flask = ">=2.2"
marshmallow = ">=3.0.0"
[package.extras]
dev = ["flask-marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
docs = ["Sphinx (==7.2.6)", "marshmallow-sqlalchemy (>=0.19.0)", "sphinx-issues (==4.0.0)"]
sqlalchemy = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.29.0)"]
tests = ["flask-marshmallow[sqlalchemy]", "pytest"]
[[package]]
name = "flask-migrate"
version = "4.0.7"
description = "SQLAlchemy database migrations for Flask applications using Alembic."
optional = false
python-versions = ">=3.6"
files = [
{file = "Flask-Migrate-4.0.7.tar.gz", hash = "sha256:dff7dd25113c210b069af280ea713b883f3840c1e3455274745d7355778c8622"},
{file = "Flask_Migrate-4.0.7-py3-none-any.whl", hash = "sha256:5c532be17e7b43a223b7500d620edae33795df27c75811ddf32560f7d48ec617"},
]
[package.dependencies]
alembic = ">=1.9.0"
Flask = ">=0.9"
Flask-SQLAlchemy = ">=1.0"
[[package]]
name = "flask-restful"
version = "0.3.10"
description = "Simple framework for creating REST APIs"
optional = false
python-versions = "*"
files = [
{file = "Flask-RESTful-0.3.10.tar.gz", hash = "sha256:fe4af2ef0027df8f9b4f797aba20c5566801b6ade995ac63b588abf1a59cec37"},
{file = "Flask_RESTful-0.3.10-py2.py3-none-any.whl", hash = "sha256:1cf93c535172f112e080b0d4503a8d15f93a48c88bdd36dd87269bdaf405051b"},
]
[package.dependencies]
aniso8601 = ">=0.82"
Flask = ">=0.8"
pytz = "*"
six = ">=1.3.0"
[package.extras]
docs = ["sphinx"]
[[package]]
name = "flask-sqlalchemy"
version = "3.1.1"
description = "Add SQLAlchemy support to your Flask application."
optional = false
python-versions = ">=3.8"
files = [
{file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"},
{file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"},
]
[package.dependencies]
flask = ">=2.2.5"
sqlalchemy = ">=2.0.16"
[[package]]
name = "greenlet"
version = "3.0.3"
description = "Lightweight in-process concurrent programming"
optional = false
python-versions = ">=3.7"
files = [
{file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"},
{file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"},
{file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"},
{file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"},
{file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"},
{file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"},
{file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"},
{file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"},
{file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"},
{file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"},
{file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"},
{file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"},
{file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"},
{file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"},
{file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"},
{file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"},
{file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"},
{file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"},
{file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"},
{file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"},
{file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"},
{file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"},
{file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"},
{file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"},
{file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"},
{file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"},
{file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"},
{file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"},
]
[package.extras]
docs = ["Sphinx", "furo"]
test = ["objgraph", "psutil"]
[[package]]
name = "itsdangerous"
version = "2.2.0"
description = "Safely pass data to untrusted environments and back."
optional = false
python-versions = ">=3.8"
files = [
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
]
[[package]]
name = "jinja2"
version = "3.1.4"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
{file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
{file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "loguru"
version = "0.7.2"
description = "Python logging made (stupidly) simple"
optional = false
python-versions = ">=3.5"
files = [
{file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
{file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
]
[package.dependencies]
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
[[package]]
name = "mako"
version = "1.3.5"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
optional = false
python-versions = ">=3.8"
files = [
{file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"},
{file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"},
]
[package.dependencies]
MarkupSafe = ">=0.9.2"
[package.extras]
babel = ["Babel"]
lingua = ["lingua"]
testing = ["pytest"]
[[package]]
name = "markupsafe"
version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.7"
files = [
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
]
[[package]]
name = "marshmallow"
version = "3.22.0"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false
python-versions = ">=3.8"
files = [
{file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"},
{file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"},
]
[package.dependencies]
packaging = ">=17.0"
[package.extras]
dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
name = "marshmallow-sqlalchemy"
version = "1.1.0"
description = "SQLAlchemy integration with the marshmallow (de)serialization library"
optional = false
python-versions = ">=3.8"
files = [
{file = "marshmallow_sqlalchemy-1.1.0-py3-none-any.whl", hash = "sha256:cce261148e4c6ec4ee275f3d29352933380a1afa2fd3933f5e9ecd02fdc16ade"},
{file = "marshmallow_sqlalchemy-1.1.0.tar.gz", hash = "sha256:2ab092da269dafa8a05d51a58409af71a8d2183958ba47143127dd239e0359d8"},
]
[package.dependencies]
marshmallow = ">=3.18.0"
SQLAlchemy = ">=1.4.40,<3.0"
[package.extras]
dev = ["marshmallow-sqlalchemy[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
docs = ["alabaster (==1.0.0)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)"]
tests = ["pytest (<9)", "pytest-lazy-fixtures"]
[[package]]
name = "packaging"
version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "pyjwt"
version = "2.9.0"
description = "JSON Web Token implementation in Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"},
{file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"},
]
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "pytz"
version = "2024.1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
files = [
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
{file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
]
[[package]]
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "sqlalchemy"
version = "2.0.32"
description = "Database Abstraction Library"
optional = false
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0c9045ecc2e4db59bfc97b20516dfdf8e41d910ac6fb667ebd3a79ea54084619"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1467940318e4a860afd546ef61fefb98a14d935cd6817ed07a228c7f7c62f389"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5954463675cb15db8d4b521f3566a017c8789222b8316b1e6934c811018ee08b"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167e7497035c303ae50651b351c28dc22a40bb98fbdb8468cdc971821b1ae533"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b27dfb676ac02529fb6e343b3a482303f16e6bc3a4d868b73935b8792edb52d0"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf2360a5e0f7bd75fa80431bf8ebcfb920c9f885e7956c7efde89031695cafb8"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-win32.whl", hash = "sha256:306fe44e754a91cd9d600a6b070c1f2fadbb4a1a257b8781ccf33c7067fd3e4d"},
{file = "SQLAlchemy-2.0.32-cp310-cp310-win_amd64.whl", hash = "sha256:99db65e6f3ab42e06c318f15c98f59a436f1c78179e6a6f40f529c8cc7100b22"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21b053be28a8a414f2ddd401f1be8361e41032d2ef5884b2f31d31cb723e559f"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b178e875a7a25b5938b53b006598ee7645172fccafe1c291a706e93f48499ff5"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723a40ee2cc7ea653645bd4cf024326dea2076673fc9d3d33f20f6c81db83e1d"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295ff8689544f7ee7e819529633d058bd458c1fd7f7e3eebd0f9268ebc56c2a0"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49496b68cd190a147118af585173ee624114dfb2e0297558c460ad7495f9dfe2"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd9b73c5c15f0ec5ce18128b1fe9157ddd0044abc373e6ecd5ba376a7e5d961"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-win32.whl", hash = "sha256:9365a3da32dabd3e69e06b972b1ffb0c89668994c7e8e75ce21d3e5e69ddef28"},
{file = "SQLAlchemy-2.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:8bd63d051f4f313b102a2af1cbc8b80f061bf78f3d5bd0843ff70b5859e27924"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bab3db192a0c35e3c9d1560eb8332463e29e5507dbd822e29a0a3c48c0a8d92"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:19d98f4f58b13900d8dec4ed09dd09ef292208ee44cc9c2fe01c1f0a2fe440e9"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd33c61513cb1b7371fd40cf221256456d26a56284e7d19d1f0b9f1eb7dd7e8"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6ba0497c1d066dd004e0f02a92426ca2df20fac08728d03f67f6960271feec"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2b6be53e4fde0065524f1a0a7929b10e9280987b320716c1509478b712a7688c"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:916a798f62f410c0b80b63683c8061f5ebe237b0f4ad778739304253353bc1cb"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-win32.whl", hash = "sha256:31983018b74908ebc6c996a16ad3690301a23befb643093fcfe85efd292e384d"},
{file = "SQLAlchemy-2.0.32-cp312-cp312-win_amd64.whl", hash = "sha256:4363ed245a6231f2e2957cccdda3c776265a75851f4753c60f3004b90e69bfeb"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8afd5b26570bf41c35c0121801479958b4446751a3971fb9a480c1afd85558e"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c750987fc876813f27b60d619b987b057eb4896b81117f73bb8d9918c14f1cad"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0102afff4890f651ed91120c1120065663506b760da4e7823913ebd3258be"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:78c03d0f8a5ab4f3034c0e8482cfcc415a3ec6193491cfa1c643ed707d476f16"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:3bd1cae7519283ff525e64645ebd7a3e0283f3c038f461ecc1c7b040a0c932a1"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-win32.whl", hash = "sha256:01438ebcdc566d58c93af0171c74ec28efe6a29184b773e378a385e6215389da"},
{file = "SQLAlchemy-2.0.32-cp37-cp37m-win_amd64.whl", hash = "sha256:4979dc80fbbc9d2ef569e71e0896990bc94df2b9fdbd878290bd129b65ab579c"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c742be912f57586ac43af38b3848f7688863a403dfb220193a882ea60e1ec3a"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:62e23d0ac103bcf1c5555b6c88c114089587bc64d048fef5bbdb58dfd26f96da"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:251f0d1108aab8ea7b9aadbd07fb47fb8e3a5838dde34aa95a3349876b5a1f1d"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef18a84e5116340e38eca3e7f9eeaaef62738891422e7c2a0b80feab165905f"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3eb6a97a1d39976f360b10ff208c73afb6a4de86dd2a6212ddf65c4a6a2347d5"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0c1c9b673d21477cec17ab10bc4decb1322843ba35b481585facd88203754fc5"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-win32.whl", hash = "sha256:c41a2b9ca80ee555decc605bd3c4520cc6fef9abde8fd66b1cf65126a6922d65"},
{file = "SQLAlchemy-2.0.32-cp38-cp38-win_amd64.whl", hash = "sha256:8a37e4d265033c897892279e8adf505c8b6b4075f2b40d77afb31f7185cd6ecd"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fec964fba2ef46476312a03ec8c425956b05c20220a1a03703537824b5e8e1"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:328429aecaba2aee3d71e11f2477c14eec5990fb6d0e884107935f7fb6001632"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85a01b5599e790e76ac3fe3aa2f26e1feba56270023d6afd5550ed63c68552b3"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf04784797dcdf4c0aa952c8d234fa01974c4729db55c45732520ce12dd95b4"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4488120becf9b71b3ac718f4138269a6be99a42fe023ec457896ba4f80749525"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14e09e083a5796d513918a66f3d6aedbc131e39e80875afe81d98a03312889e6"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-win32.whl", hash = "sha256:0d322cc9c9b2154ba7e82f7bf25ecc7c36fbe2d82e2933b3642fc095a52cfc78"},
{file = "SQLAlchemy-2.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:7dd8583df2f98dea28b5cd53a1beac963f4f9d087888d75f22fcc93a07cf8d84"},
{file = "SQLAlchemy-2.0.32-py3-none-any.whl", hash = "sha256:e567a8793a692451f706b363ccf3c45e056b67d90ead58c3bc9471af5d212202"},
{file = "SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8"},
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
typing-extensions = ">=4.6.0"
[package.extras]
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
aioodbc = ["aioodbc", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"]
mssql-pyodbc = ["pyodbc"]
mypy = ["mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0)"]
mysql-connector = ["mysql-connector-python"]
oracle = ["cx_oracle (>=8)"]
oracle-oracledb = ["oracledb (>=1.0.1)"]
postgresql = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
postgresql-psycopg = ["psycopg (>=3.0.7)"]
postgresql-psycopg2binary = ["psycopg2-binary"]
postgresql-psycopg2cffi = ["psycopg2cffi"]
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3_binary"]
[[package]]
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
name = "werkzeug"
version = "3.0.4"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.8"
files = [
{file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"},
{file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"},
]
[package.dependencies]
MarkupSafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog (>=2.3)"]
[[package]]
name = "win32-setctime"
version = "1.1.0"
description = "A small Python utility to set file creation time on Windows"
optional = false
python-versions = ">=3.5"
files = [
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
]
[package.extras]
dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "dda1a445d3e4ac500b0e776fae43a153624d8d62cb91ca0f2584837b622a42a7"
projects/web_demo/pyproject.toml
deleted
100644 → 0
View file @
3cc3f754
[tool.poetry]
name
=
"web-api"
version
=
"0.1.0"
description
=
""
authors
=
[
"houlinfeng <m15237195947@163.com>"
]
readme
=
"README.md"
[tool.poetry.dependencies]
python
=
"^3.10"
flask
=
"^3.0.3"
flask-restful
=
"^0.3.10"
flask-cors
=
"^5.0.0"
flask-sqlalchemy
=
"^3.1.1"
flask-migrate
=
"^4.0.7"
flask-jwt-extended
=
"^4.6.0"
flask-marshmallow
=
"^1.2.1"
pyyaml
=
"^6.0.2"
loguru
=
"^0.7.2"
marshmallow-sqlalchemy
=
"^1.1.0"
[build-system]
requires
=
["poetry-core"]
build-backend
=
"poetry.core.masonry.api"
projects/web_demo/requirements.txt
deleted
100644 → 0
View file @
3cc3f754
flask-cors
flask-jwt-extended
flask-marshmallow
flask-migrate
flask-restful
flask-sqlalchemy
flask
greenlet
loguru
marshmallow-sqlalchemy
marshmallow
pyjwt
pyyaml
projects/web_demo/tests/__init__.py
deleted
100644 → 0
View file @
3cc3f754
projects/web_demo/web_demo/__init__.py
deleted
100644 → 0
View file @
3cc3f754
__all__
=
[
"common"
,
"api"
]
\ No newline at end of file
projects/web_demo/web_demo/api/__init__.py
deleted
100644 → 0
View file @
3cc3f754
import
os
from
.extentions
import
app
,
db
,
migrate
,
jwt
,
ma
from
common.web_hook
import
before_request
from
common.logger
import
setup_log
root_dir
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
)))
def
_register_db
(
flask_app
):
from
common
import
import_models
db
.
init_app
(
flask_app
)
with
app
.
app_context
():
db
.
create_all
()
def
create_app
(
config
):
"""
Create and configure an instance of the Flask application
:param config:
:return:
"""
app
.
static_folder
=
os
.
path
.
join
(
root_dir
,
"static"
)
if
config
is
None
:
config
=
{}
app
.
config
.
update
(
config
)
setup_log
(
config
)
_register_db
(
app
)
migrate
.
init_app
(
app
=
app
,
db
=
db
)
jwt
.
init_app
(
app
=
app
)
ma
.
init_app
(
app
=
app
)
from
.analysis
import
analysis_blue
app
.
register_blueprint
(
analysis_blue
)
from
.react_app
import
react_app_blue
app
.
register_blueprint
(
react_app_blue
)
app
.
before_request
(
before_request
)
return
app
projects/web_demo/web_demo/api/analysis/__init__.py
deleted
100644 → 0
View file @
3cc3f754
from
flask
import
Blueprint
from
..extentions
import
Api
from
.upload_view
import
UploadPdfView
from
.analysis_view
import
AnalysisTaskView
,
AnalysisTaskProgressView
from
.img_md_view
import
ImgView
,
MdView
from
.task_view
import
TaskView
,
HistoricalTasksView
,
DeleteTaskView
from
.markdown_view
import
MarkdownView
analysis_blue
=
Blueprint
(
'analysis'
,
__name__
)
api_v2
=
Api
(
analysis_blue
,
prefix
=
'/api/v2'
)
api_v2
.
add_resource
(
UploadPdfView
,
'/analysis/upload_pdf'
)
api_v2
.
add_resource
(
AnalysisTaskView
,
'/extract/task/submit'
)
api_v2
.
add_resource
(
AnalysisTaskProgressView
,
'/extract/task/progress'
)
api_v2
.
add_resource
(
ImgView
,
'/analysis/pdf_img'
)
api_v2
.
add_resource
(
MdView
,
'/analysis/pdf_md'
)
api_v2
.
add_resource
(
TaskView
,
'/extract/taskQueue'
)
api_v2
.
add_resource
(
HistoricalTasksView
,
'/extract/list'
)
api_v2
.
add_resource
(
DeleteTaskView
,
'/extract/task/<int:id>'
)
api_v2
.
add_resource
(
MarkdownView
,
'/extract/markdown'
)
\ No newline at end of file
projects/web_demo/web_demo/api/analysis/analysis_view.py
deleted
100644 → 0
View file @
3cc3f754
import
json
import
threading
from
multiprocessing
import
Process
from
pathlib
import
Path
from
flask
import
request
,
current_app
,
url_for
from
flask_restful
import
Resource
from
.ext
import
find_file
,
task_state_map
# from .formula_ext import formula_detection, formula_recognition
from
.serialization
import
AnalysisViewSchema
from
marshmallow
import
ValidationError
from
..extentions
import
db
from
.models
import
AnalysisTask
,
AnalysisPdf
from
.pdf_ext
import
analysis_pdf_task
from
common.custom_response
import
generate_response
class
AnalysisTaskProgressView
(
Resource
):
def
get
(
self
):
"""
获取任务进度
:return:
"""
params
=
request
.
args
id
=
params
.
get
(
'id'
)
analysis_task
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
id
==
id
).
first
()
if
not
analysis_task
:
return
generate_response
(
code
=
400
,
msg
=
"Invalid ID"
,
msgZH
=
"无效id"
)
match
analysis_task
.
task_type
:
case
'pdf'
:
analysis_pdf
=
AnalysisPdf
.
query
.
filter
(
AnalysisPdf
.
id
==
analysis_task
.
analysis_pdf_id
).
first
()
file_url
=
url_for
(
'analysis.uploadpdfview'
,
filename
=
analysis_task
.
file_name
,
as_attachment
=
False
)
file_name_split
=
analysis_task
.
file_name
.
split
(
"_"
)
file_name
=
file_name_split
[
-
1
]
if
file_name_split
else
analysis_task
.
file_name
if
analysis_task
.
status
==
0
:
data
=
{
"state"
:
task_state_map
.
get
(
analysis_task
.
status
),
"status"
:
analysis_pdf
.
status
,
"url"
:
file_url
,
"fileName"
:
file_name
,
"file_key"
:
analysis_task
.
file_key
,
"content"
:
[],
"markdownUrl"
:
[],
"fullMdLink"
:
""
,
"type"
:
analysis_task
.
task_type
,
}
return
generate_response
(
data
=
data
)
elif
analysis_task
.
status
==
1
:
if
analysis_pdf
.
status
==
1
:
# 任务正常完成
bbox_info
=
json
.
loads
(
analysis_pdf
.
bbox_info
)
md_link_list
=
json
.
loads
(
analysis_pdf
.
md_link_list
)
full_md_link
=
analysis_pdf
.
full_md_link
data
=
{
"state"
:
task_state_map
.
get
(
analysis_task
.
status
),
"status"
:
analysis_pdf
.
status
,
"url"
:
file_url
,
"fileName"
:
file_name
,
"file_key"
:
analysis_task
.
file_key
,
"content"
:
bbox_info
,
"markdownUrl"
:
md_link_list
,
"fullMdLink"
:
full_md_link
,
"type"
:
analysis_task
.
task_type
,
}
return
generate_response
(
data
=
data
)
else
:
# 任务异常结束
data
=
{
"state"
:
"failed"
,
"status"
:
analysis_pdf
.
status
,
"url"
:
file_url
,
"fileName"
:
file_name
,
"file_key"
:
analysis_task
.
file_key
,
"content"
:
[],
"markdownUrl"
:
[],
"fullMdLink"
:
""
,
"type"
:
analysis_task
.
task_type
,
}
return
generate_response
(
code
=-
60004
,
data
=
data
,
msg
=
"Failed to retrieve PDF parsing progress"
,
msgZh
=
"无法获取PDF解析进度"
)
else
:
data
=
{
"state"
:
task_state_map
.
get
(
analysis_task
.
status
),
"status"
:
analysis_pdf
.
status
,
"url"
:
file_url
,
"fileName"
:
file_name
,
"file_key"
:
analysis_task
.
file_key
,
"content"
:
[],
"markdownUrl"
:
[],
"fullMdLink"
:
""
,
"type"
:
analysis_task
.
task_type
,
}
return
generate_response
(
data
=
data
)
case
'formula-detect'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
'formula-extract'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
'table-recogn'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
_
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"参数不支持"
)
class
AnalysisTaskView
(
Resource
):
def
post
(
self
):
"""
提交任务
:return:
"""
analysis_view_schema
=
AnalysisViewSchema
()
try
:
params
=
analysis_view_schema
.
load
(
request
.
get_json
())
except
ValidationError
as
err
:
return
generate_response
(
code
=
400
,
msg
=
err
.
messages
)
file_key
=
params
.
get
(
"fileKey"
)
file_name
=
params
.
get
(
"fileName"
)
task_type
=
params
.
get
(
"taskType"
)
is_ocr
=
params
.
get
(
"isOcr"
,
False
)
pdf_upload_folder
=
current_app
.
config
[
'PDF_UPLOAD_FOLDER'
]
upload_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_upload_folder
}
"
file_path
=
find_file
(
file_key
,
upload_dir
)
match
task_type
:
case
'pdf'
:
if
not
file_path
:
return
generate_response
(
code
=
400
,
msg
=
"FileKey is invalid, no PDF file found"
,
msgZH
=
"fileKey无效,未找到pdf文件"
)
analysis_task
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
status
.
in_
([
0
,
2
])).
first
()
file_name
=
Path
(
file_path
).
name
with
db
.
auto_commit
():
analysis_pdf_object
=
AnalysisPdf
(
file_name
=
file_name
,
file_path
=
file_path
,
status
=
3
if
analysis_task
else
0
,
)
db
.
session
.
add
(
analysis_pdf_object
)
db
.
session
.
flush
()
analysis_pdf_id
=
analysis_pdf_object
.
id
with
db
.
auto_commit
():
analysis_task_object
=
AnalysisTask
(
file_key
=
file_key
,
file_name
=
file_name
,
task_type
=
task_type
,
is_ocr
=
is_ocr
,
status
=
2
if
analysis_task
else
0
,
analysis_pdf_id
=
analysis_pdf_id
)
db
.
session
.
add
(
analysis_task_object
)
db
.
session
.
flush
()
analysis_task_id
=
analysis_task_object
.
id
if
not
analysis_task
:
# 已有同类型任务在执行,请等待执行完成
file_stem
=
Path
(
file_path
).
stem
pdf_analysis_folder
=
current_app
.
config
[
'PDF_ANALYSIS_FOLDER'
]
pdf_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_analysis_folder
}
/
{
file_stem
}
"
image_dir
=
f
"
{
pdf_dir
}
/images"
t
=
threading
.
Thread
(
target
=
analysis_pdf_task
,
args
=
(
pdf_dir
,
image_dir
,
file_path
,
is_ocr
,
analysis_pdf_id
))
t
.
start
()
# 生成文件的URL路径
file_url
=
url_for
(
'analysis.uploadpdfview'
,
filename
=
file_name
,
as_attachment
=
False
)
data
=
{
"url"
:
file_url
,
"fileName"
:
file_name
,
"id"
:
analysis_task_id
}
return
generate_response
(
data
=
data
)
case
'formula-detect'
:
# if not file_path:
# return generate_response(code=400, msg="FileKey is invalid, no image file found",
# msgZH="fileKey无效,未找到图片")
# return formula_detection(file_path, upload_dir)
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
'formula-extract'
:
# if not file_path:
# return generate_response(code=400, msg="FileKey is invalid, no image file found",
# msgZH="fileKey无效,未找到图片")
# return formula_recognition(file_path, upload_dir)
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
'table-recogn'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
_
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"参数不支持"
)
def
put
(
self
):
"""
重新发起任务
:return:
"""
params
=
json
.
loads
(
request
.
data
)
id
=
params
.
get
(
'id'
)
analysis_task
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
id
==
id
).
first
()
if
not
analysis_task
:
return
generate_response
(
code
=
400
,
msg
=
"Invalid ID"
,
msgZH
=
"无效id"
)
match
analysis_task
.
task_type
:
case
'pdf'
:
task_r_p
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
status
.
in_
([
0
,
2
])).
first
()
if
task_r_p
:
with
db
.
auto_commit
():
analysis_pdf_object
=
AnalysisPdf
.
query
.
filter_by
(
id
=
analysis_task
.
analysis_pdf_id
).
first
()
analysis_pdf_object
.
status
=
3
db
.
session
.
add
(
analysis_pdf_object
)
with
db
.
auto_commit
():
analysis_task
.
status
=
2
db
.
session
.
add
(
analysis_task
)
else
:
with
db
.
auto_commit
():
analysis_pdf_object
=
AnalysisPdf
.
query
.
filter_by
(
id
=
analysis_task
.
analysis_pdf_id
).
first
()
analysis_pdf_object
.
status
=
0
db
.
session
.
add
(
analysis_pdf_object
)
with
db
.
auto_commit
():
analysis_task
.
status
=
0
db
.
session
.
add
(
analysis_task
)
pdf_upload_folder
=
current_app
.
config
[
'PDF_UPLOAD_FOLDER'
]
upload_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_upload_folder
}
"
file_path
=
find_file
(
analysis_task
.
file_key
,
upload_dir
)
file_stem
=
Path
(
file_path
).
stem
pdf_analysis_folder
=
current_app
.
config
[
'PDF_ANALYSIS_FOLDER'
]
pdf_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_analysis_folder
}
/
{
file_stem
}
"
image_dir
=
f
"
{
pdf_dir
}
/images"
process
=
Process
(
target
=
analysis_pdf_task
,
args
=
(
pdf_dir
,
image_dir
,
file_path
,
analysis_task
.
is_ocr
,
analysis_task
.
analysis_pdf_id
))
process
.
start
()
# 生成文件的URL路径
file_url
=
url_for
(
'analysis.uploadpdfview'
,
filename
=
analysis_task
.
file_name
,
as_attachment
=
False
)
file_name_split
=
analysis_task
.
file_name
.
split
(
"_"
)
new_file_name
=
file_name_split
[
-
1
]
if
file_name_split
else
analysis_task
.
file_name
data
=
{
"url"
:
file_url
,
"fileName"
:
new_file_name
,
"id"
:
analysis_task
.
id
}
return
generate_response
(
data
=
data
)
case
'formula-detect'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
'formula-extract'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
'table-recogn'
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"功能待开发"
)
case
_
:
return
generate_response
(
code
=
400
,
msg
=
"Not yet supported"
,
msgZH
=
"参数不支持"
)
projects/web_demo/web_demo/api/analysis/ext.py
deleted
100644 → 0
View file @
3cc3f754
import
os
task_state_map
=
{
0
:
"running"
,
1
:
"done"
,
2
:
"pending"
}
def
find_file
(
file_key
,
file_dir
):
"""
查询文件
:param file_key: 文件哈希
:param file_dir: 文件目录
:return:
"""
pdf_path
=
""
for
root
,
subDirs
,
files
in
os
.
walk
(
file_dir
):
for
fileName
in
files
:
if
fileName
.
startswith
(
file_key
):
pdf_path
=
os
.
path
.
join
(
root
,
fileName
)
break
if
pdf_path
:
break
return
pdf_path
projects/web_demo/web_demo/api/analysis/formula_ext.py
deleted
100644 → 0
View file @
3cc3f754
import
os
import
pkgutil
import
numpy
as
np
import
yaml
import
argparse
import
cv2
from
pathlib
import
Path
from
ultralytics
import
YOLO
from
unimernet.common.config
import
Config
import
unimernet.tasks
as
tasks
from
unimernet.processors
import
load_processor
from
magic_pdf.libs.config_reader
import
get_local_models_dir
,
get_device
from
torchvision
import
transforms
from
magic_pdf.pre_proc.ocr_span_list_modify
import
remove_overlaps_low_confidence_spans
,
remove_overlaps_min_spans
from
PIL
import
Image
from
common.ext
import
singleton_func
from
common.custom_response
import
generate_response
def
mfd_model_init
(
weight
):
mfd_model
=
YOLO
(
weight
)
return
mfd_model
def
mfr_model_init
(
weight_dir
,
cfg_path
,
_device_
=
'cpu'
):
args
=
argparse
.
Namespace
(
cfg_path
=
cfg_path
,
options
=
None
)
cfg
=
Config
(
args
)
cfg
.
config
.
model
.
pretrained
=
os
.
path
.
join
(
weight_dir
,
"pytorch_model.bin"
)
cfg
.
config
.
model
.
model_config
.
model_name
=
weight_dir
cfg
.
config
.
model
.
tokenizer_config
.
path
=
weight_dir
task
=
tasks
.
setup_task
(
cfg
)
model
=
task
.
build_model
(
cfg
)
model
=
model
.
to
(
_device_
)
vis_processor
=
load_processor
(
'formula_image_eval'
,
cfg
.
config
.
datasets
.
formula_rec_eval
.
vis_processor
.
eval
)
return
model
,
vis_processor
@
singleton_func
class
CustomPEKModel
:
def
__init__
(
self
):
# PDF-Extract-Kit/models
models_dir
=
get_local_models_dir
()
self
.
device
=
get_device
()
loader
=
pkgutil
.
get_loader
(
"magic_pdf"
)
root_dir
=
Path
(
loader
.
path
).
parent
# model_config目录
model_config_dir
=
os
.
path
.
join
(
root_dir
,
'resources'
,
'model_config'
)
# 构建 model_configs.yaml 文件的完整路径
config_path
=
os
.
path
.
join
(
model_config_dir
,
'model_configs.yaml'
)
with
open
(
config_path
,
"r"
,
encoding
=
'utf-8'
)
as
f
:
configs
=
yaml
.
load
(
f
,
Loader
=
yaml
.
FullLoader
)
# 初始化公式检测模型
self
.
mfd_model
=
mfd_model_init
(
str
(
os
.
path
.
join
(
models_dir
,
configs
[
"weights"
][
"mfd"
])))
# 初始化公式解析模型
mfr_weight_dir
=
str
(
os
.
path
.
join
(
models_dir
,
configs
[
"weights"
][
"mfr"
]))
mfr_cfg_path
=
str
(
os
.
path
.
join
(
model_config_dir
,
"UniMERNet"
,
"demo.yaml"
))
self
.
mfr_model
,
mfr_vis_processors
=
mfr_model_init
(
mfr_weight_dir
,
mfr_cfg_path
,
_device_
=
self
.
device
)
self
.
mfr_transform
=
transforms
.
Compose
([
mfr_vis_processors
,
])
def
get_all_spans
(
layout_dets
)
->
list
:
def
remove_duplicate_spans
(
spans
):
new_spans
=
[]
for
span
in
spans
:
if
not
any
(
span
==
existing_span
for
existing_span
in
new_spans
):
new_spans
.
append
(
span
)
return
new_spans
all_spans
=
[]
# allow_category_id_list = [3, 5, 13, 14, 15]
"""当成span拼接的"""
# 3: 'image', # 图片
# 5: 'table', # 表格
# 13: 'inline_equation', # 行内公式
# 14: 'interline_equation', # 行间公式
# 15: 'text', # ocr识别文本
for
layout_det
in
layout_dets
:
if
layout_det
.
get
(
"bbox"
)
is
not
None
:
# 兼容直接输出bbox的模型数据,如paddle
x0
,
y0
,
x1
,
y1
=
layout_det
[
"bbox"
]
else
:
# 兼容直接输出poly的模型数据,如xxx
x0
,
y0
,
_
,
_
,
x1
,
y1
,
_
,
_
=
layout_det
[
"poly"
]
bbox
=
[
x0
,
y0
,
x1
,
y1
]
layout_det
[
"bbox"
]
=
bbox
all_spans
.
append
(
layout_det
)
return
remove_duplicate_spans
(
all_spans
)
def
formula_predict
(
mfd_model
,
image
):
"""
公式检测
:param mfd_model:
:param image:
:return:
"""
latex_filling_list
=
[]
# 公式检测
mfd_res
=
mfd_model
.
predict
(
image
,
imgsz
=
1888
,
conf
=
0.25
,
iou
=
0.45
,
verbose
=
True
)[
0
]
for
xyxy
,
conf
,
cla
in
zip
(
mfd_res
.
boxes
.
xyxy
.
cpu
(),
mfd_res
.
boxes
.
conf
.
cpu
(),
mfd_res
.
boxes
.
cls
.
cpu
()):
xmin
,
ymin
,
xmax
,
ymax
=
[
int
(
p
.
item
())
for
p
in
xyxy
]
new_item
=
{
'category_id'
:
13
+
int
(
cla
.
item
()),
'poly'
:
[
xmin
,
ymin
,
xmax
,
ymin
,
xmax
,
ymax
,
xmin
,
ymax
],
'score'
:
round
(
float
(
conf
.
item
()),
2
),
'latex'
:
''
,
}
latex_filling_list
.
append
(
new_item
)
return
latex_filling_list
def
formula_detection
(
file_path
,
upload_dir
):
"""
公式检测
:param file_path: 文件路径
:param upload_dir: 上传文件夹
:return:
"""
try
:
image_open
=
Image
.
open
(
file_path
)
except
IOError
:
return
generate_response
(
code
=
400
,
msg
=
"params is not valid"
,
msgZh
=
"参数类型不是图片,无效参数"
)
filename
=
Path
(
file_path
).
name
# 获取图片宽高
width
,
height
=
image_open
.
size
# 转换为RGB,忽略透明度通道
rgb_image
=
image_open
.
convert
(
'RGB'
)
# 保存转换后的图片
rgb_image
.
save
(
file_path
)
# 初始化模型
cpm
=
CustomPEKModel
()
# 初始化公式检测模型
mfd_model
=
cpm
.
mfd_model
image_conv
=
Image
.
open
(
file_path
)
image_array
=
np
.
array
(
image_conv
)
pdf_width
=
1416
pdf_height
=
1888
# 重置图片大小
scale
=
min
(
pdf_width
//
2
/
width
,
pdf_height
//
2
/
height
)
# 缩放比例
nw
=
int
(
width
*
scale
)
nh
=
int
(
height
*
scale
)
image_resize
=
cv2
.
resize
(
image_array
,
(
nw
,
nh
),
interpolation
=
cv2
.
INTER_LINEAR
)
resize_image_path
=
f
"
{
upload_dir
}
/resize_
{
filename
}
"
cv2
.
imwrite
(
resize_image_path
,
image_resize
)
# 将重置的图片贴到pdf白纸中
x
=
(
pdf_width
-
nw
)
//
2
y
=
(
pdf_height
-
nh
)
//
2
new_img
=
Image
.
new
(
'RGB'
,
(
pdf_width
,
pdf_height
),
'white'
)
image_scale
=
Image
.
open
(
resize_image_path
)
new_img
.
paste
(
image_scale
,
(
x
,
y
))
# 公式检测
latex_filling_list
=
formula_predict
(
mfd_model
,
new_img
)
os
.
remove
(
resize_image_path
)
# 将缩放图公式检测的坐标还原为原图公式检测的坐标
for
item
in
latex_filling_list
:
item_poly
=
item
[
"poly"
]
item
[
"poly"
]
=
[
(
item_poly
[
0
]
-
x
)
/
scale
,
(
item_poly
[
1
]
-
y
)
/
scale
,
(
item_poly
[
2
]
-
x
)
/
scale
,
(
item_poly
[
3
]
-
y
)
/
scale
,
(
item_poly
[
4
]
-
x
)
/
scale
,
(
item_poly
[
5
]
-
y
)
/
scale
,
(
item_poly
[
6
]
-
x
)
/
scale
,
(
item_poly
[
7
]
-
y
)
/
scale
,
]
if
not
latex_filling_list
:
return
generate_response
(
code
=
1001
,
msg
=
"detection fail"
,
msgZh
=
"公式检测失败,图片过小,无法检测"
)
spans
=
get_all_spans
(
latex_filling_list
)
'''删除重叠spans中置信度较低的那些'''
spans
,
dropped_spans_by_confidence
=
remove_overlaps_low_confidence_spans
(
spans
)
'''删除重叠spans中较小的那些'''
spans
,
dropped_spans_by_span_overlap
=
remove_overlaps_min_spans
(
spans
)
return
generate_response
(
data
=
{
'layout'
:
spans
,
})
def
formula_recognition
(
file_path
,
upload_dir
):
"""
公式识别
:param file_path: 文件路径
:param upload_dir: 上传文件夹
:return:
"""
try
:
image_open
=
Image
.
open
(
file_path
)
except
IOError
:
return
generate_response
(
code
=
400
,
msg
=
"params is not valid"
,
msgZh
=
"参数类型不是图片,无效参数"
)
filename
=
Path
(
file_path
).
name
# 获取图片宽高
width
,
height
=
image_open
.
size
# 转换为RGB,忽略透明度通道
rgb_image
=
image_open
.
convert
(
'RGB'
)
# 保存转换后的图片
rgb_image
.
save
(
file_path
)
image_conv
=
Image
.
open
(
file_path
)
image_array
=
np
.
array
(
image_conv
)
pdf_width
=
1416
pdf_height
=
1888
# 重置图片大小
scale
=
min
(
pdf_width
//
2
/
width
,
pdf_height
//
2
/
height
)
# 缩放比例
nw
=
int
(
width
*
scale
)
nh
=
int
(
height
*
scale
)
image_resize
=
cv2
.
resize
(
image_array
,
(
nw
,
nh
),
interpolation
=
cv2
.
INTER_LINEAR
)
resize_image_path
=
f
"
{
upload_dir
}
/resize_
{
filename
}
"
cv2
.
imwrite
(
resize_image_path
,
image_resize
)
# 将重置的图片贴到pdf白纸中
x
=
(
pdf_width
-
nw
)
//
2
y
=
(
pdf_height
-
nh
)
//
2
new_img
=
Image
.
new
(
'RGB'
,
(
pdf_width
,
pdf_height
),
'white'
)
image_scale
=
Image
.
open
(
resize_image_path
)
new_img
.
paste
(
image_scale
,
(
x
,
y
))
new_img_array
=
np
.
array
(
new_img
)
# 初始化模型
cpm
=
CustomPEKModel
()
# device
device
=
cpm
.
device
# 初始化公式检测模型
mfd_model
=
cpm
.
mfd_model
# 初始化公式解析模型
mfr_model
=
cpm
.
mfr_model
mfr_transform
=
cpm
.
mfr_transform
# 公式识别
latex_filling_list
,
mfr_res
=
formula_recognition
(
mfd_model
,
new_img_array
,
mfr_transform
,
device
,
mfr_model
,
image_open
)
os
.
remove
(
resize_image_path
)
# 将缩放图公式检测的坐标还原为原图公式检测的坐标
for
item
in
latex_filling_list
:
item_poly
=
item
[
"poly"
]
item
[
"poly"
]
=
[
(
item_poly
[
0
]
-
x
)
/
scale
,
(
item_poly
[
1
]
-
y
)
/
scale
,
(
item_poly
[
2
]
-
x
)
/
scale
,
(
item_poly
[
3
]
-
y
)
/
scale
,
(
item_poly
[
4
]
-
x
)
/
scale
,
(
item_poly
[
5
]
-
y
)
/
scale
,
(
item_poly
[
6
]
-
x
)
/
scale
,
(
item_poly
[
7
]
-
y
)
/
scale
,
]
spans
=
get_all_spans
(
latex_filling_list
)
'''删除重叠spans中置信度较低的那些'''
spans
,
dropped_spans_by_confidence
=
remove_overlaps_low_confidence_spans
(
spans
)
'''删除重叠spans中较小的那些'''
spans
,
dropped_spans_by_span_overlap
=
remove_overlaps_min_spans
(
spans
)
if
not
latex_filling_list
:
width
,
height
=
image_open
.
size
latex_filling_list
.
append
({
'category_id'
:
14
,
'poly'
:
[
0
,
0
,
width
,
0
,
width
,
height
,
0
,
height
],
'score'
:
1
,
'latex'
:
mfr_res
[
0
]
if
mfr_res
else
""
,
})
return
generate_response
(
data
=
{
'layout'
:
spans
if
spans
else
latex_filling_list
,
"mfr_res"
:
mfr_res
})
projects/web_demo/web_demo/api/analysis/img_md_view.py
deleted
100644 → 0
View file @
3cc3f754
from
pathlib
import
Path
from
flask
import
request
,
current_app
,
send_from_directory
from
flask_restful
import
Resource
class
ImgView
(
Resource
):
def
get
(
self
):
"""
获取pdf解析的图片
:return:
"""
params
=
request
.
args
pdf
=
params
.
get
(
'pdf'
)
filename
=
params
.
get
(
'filename'
)
as_attachment
=
params
.
get
(
'as_attachment'
)
if
str
(
as_attachment
).
lower
()
==
"true"
:
as_attachment
=
True
else
:
as_attachment
=
False
file_stem
=
Path
(
pdf
).
stem
pdf_analysis_folder
=
current_app
.
config
[
'PDF_ANALYSIS_FOLDER'
]
pdf_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_analysis_folder
}
/
{
file_stem
}
"
image_dir
=
f
"
{
pdf_dir
}
/images"
response
=
send_from_directory
(
image_dir
,
filename
,
as_attachment
=
as_attachment
)
return
response
class
MdView
(
Resource
):
def
get
(
self
):
"""
获取pdf解析的markdown
:return:
"""
params
=
request
.
args
pdf
=
params
.
get
(
'pdf'
)
filename
=
params
.
get
(
'filename'
)
as_attachment
=
params
.
get
(
'as_attachment'
)
if
str
(
as_attachment
).
lower
()
==
"true"
:
as_attachment
=
True
else
:
as_attachment
=
False
file_stem
=
Path
(
pdf
).
stem
pdf_analysis_folder
=
current_app
.
config
[
'PDF_ANALYSIS_FOLDER'
]
pdf_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_analysis_folder
}
/
{
file_stem
}
"
response
=
send_from_directory
(
pdf_dir
,
filename
,
as_attachment
=
as_attachment
)
return
response
projects/web_demo/web_demo/api/analysis/markdown_view.py
deleted
100644 → 0
View file @
3cc3f754
import
json
from
pathlib
import
Path
from
flask
import
request
,
current_app
from
flask_restful
import
Resource
from
common.custom_response
import
generate_response
class
MarkdownView
(
Resource
):
def
put
(
self
):
"""
编辑markdown
"""
params
=
json
.
loads
(
request
.
data
)
file_key
=
params
.
get
(
'file_key'
)
data
=
params
.
get
(
'data'
,
{})
if
not
data
:
return
generate_response
(
code
=
400
,
msg
=
"empty data"
,
msgZH
=
"数据为空,无法更新markdown"
)
pdf_analysis_folder
=
current_app
.
config
[
'PDF_ANALYSIS_FOLDER'
]
pdf_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_analysis_folder
}
"
markdown_file_dir
=
""
for
path_obj
in
Path
(
pdf_dir
).
iterdir
():
if
path_obj
.
name
.
startswith
(
file_key
):
markdown_file_dir
=
path_obj
break
if
markdown_file_dir
and
Path
(
markdown_file_dir
).
exists
():
for
k
,
v
in
data
.
items
():
md_path
=
f
"
{
markdown_file_dir
}
/
{
k
}
.md"
if
Path
(
md_path
).
exists
():
with
open
(
md_path
,
'w'
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
v
)
full_content
=
""
for
path_obj
in
Path
(
markdown_file_dir
).
iterdir
():
if
path_obj
.
is_file
()
and
path_obj
.
suffix
==
".md"
and
path_obj
.
stem
!=
"full"
:
with
open
(
path_obj
,
'r'
,
encoding
=
"utf-8"
)
as
f
:
full_content
+=
f
.
read
()
+
"
\n
"
with
open
(
f
"
{
markdown_file_dir
}
/full.md"
,
'w'
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
full_content
)
else
:
return
generate_response
(
code
=
400
,
msg
=
"Invalid file_key"
,
msgZH
=
"文件哈希错误"
)
return
generate_response
()
projects/web_demo/web_demo/api/analysis/models.py
deleted
100644 → 0
View file @
3cc3f754
from
datetime
import
datetime
from
..extentions
import
db
class
AnalysisTask
(
db
.
Model
):
__tablename__
=
'analysis_task'
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
,
autoincrement
=
True
)
file_key
=
db
.
Column
(
db
.
Text
,
comment
=
"文件唯一哈希"
)
file_name
=
db
.
Column
(
db
.
Text
,
comment
=
"文件名称"
)
task_type
=
db
.
Column
(
db
.
String
(
128
),
comment
=
"任务类型"
)
is_ocr
=
db
.
Column
(
db
.
Boolean
,
default
=
False
,
comment
=
"是否ocr"
)
status
=
db
.
Column
(
db
.
Integer
,
default
=
0
,
comment
=
"状态"
)
# 0 running 1 done 2 pending
analysis_pdf_id
=
db
.
Column
(
db
.
Integer
,
comment
=
"analysis_pdf的id"
)
create_date
=
db
.
Column
(
db
.
DateTime
(),
nullable
=
False
,
default
=
datetime
.
now
)
update_date
=
db
.
Column
(
db
.
DateTime
(),
nullable
=
False
,
default
=
datetime
.
now
,
onupdate
=
datetime
.
now
)
class
AnalysisPdf
(
db
.
Model
):
__tablename__
=
'analysis_pdf'
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
,
autoincrement
=
True
)
file_name
=
db
.
Column
(
db
.
Text
,
comment
=
"文件名称"
)
file_url
=
db
.
Column
(
db
.
Text
,
comment
=
"文件原路径"
)
file_path
=
db
.
Column
(
db
.
Text
,
comment
=
"文件路径"
)
status
=
db
.
Column
(
db
.
Integer
,
default
=
3
,
comment
=
"状态"
)
# 0 转换中 1 已完成 2 转换失败 3 init
bbox_info
=
db
.
Column
(
db
.
Text
,
comment
=
"坐标数据"
)
md_link_list
=
db
.
Column
(
db
.
Text
,
comment
=
"markdown分页链接"
)
full_md_link
=
db
.
Column
(
db
.
Text
,
comment
=
"markdown全文链接"
)
create_date
=
db
.
Column
(
db
.
DateTime
(),
nullable
=
False
,
default
=
datetime
.
now
)
update_date
=
db
.
Column
(
db
.
DateTime
(),
nullable
=
False
,
default
=
datetime
.
now
,
onupdate
=
datetime
.
now
)
\ No newline at end of file
projects/web_demo/web_demo/api/analysis/pdf_ext.py
deleted
100644 → 0
View file @
3cc3f754
import
json
import
os
import
shutil
import
traceback
from
pathlib
import
Path
from
common.error_types
import
ApiException
from
common.mk_markdown.mk_markdown
import
\
ocr_mk_mm_markdown_with_para_and_pagination
from
flask
import
current_app
,
url_for
from
loguru
import
logger
import
magic_pdf.model
as
model_config
from
magic_pdf.config.enums
import
SupportedPdfParseMethod
from
magic_pdf.data.data_reader_writer
import
FileBasedDataWriter
from
magic_pdf.data.dataset
import
PymuDocDataset
from
magic_pdf.libs.json_compressor
import
JsonCompressor
from
magic_pdf.model.doc_analyze_by_custom_model
import
doc_analyze
from
magic_pdf.operators.models
import
InferenceResult
from
..extentions
import
app
,
db
from
.ext
import
find_file
from
.models
import
AnalysisPdf
,
AnalysisTask
model_config
.
__use_inside_model__
=
True
def
analysis_pdf
(
image_url_prefix
,
image_dir
,
pdf_bytes
,
is_ocr
=
False
):
try
:
model_json
=
[]
# model_json传空list使用内置模型解析
image_writer
=
FileBasedDataWriter
(
image_dir
)
logger
.
info
(
f
'is_ocr:
{
is_ocr
}
'
)
parse_method
=
'ocr'
ds
=
PymuDocDataset
(
pdf_bytes
)
# Choose parsing method
if
not
is_ocr
:
if
ds
.
classify
()
==
SupportedPdfParseMethod
.
OCR
:
parse_method
=
'ocr'
else
:
parse_method
=
'txt'
if
parse_method
==
'ocr'
:
infer_result
=
ds
.
apply
(
doc_analyze
,
ocr
=
True
)
else
:
infer_result
=
ds
.
apply
(
doc_analyze
,
ocr
=
False
)
if
parse_method
==
'ocr'
:
pipe_res
=
infer_result
.
pipe_ocr_mode
(
image_writer
)
else
:
pipe_res
=
infer_result
.
pipe_txt_mode
(
image_writer
)
pdf_mid_data
=
pipe_res
.
_pipe_res
pdf_info_list
=
pdf_mid_data
[
'pdf_info'
]
md_content
=
json
.
dumps
(
ocr_mk_mm_markdown_with_para_and_pagination
(
pdf_info_list
,
image_url_prefix
),
ensure_ascii
=
False
)
bbox_info
=
get_bbox_info
(
pdf_info_list
)
return
md_content
,
bbox_info
except
Exception
as
e
:
# noqa: F841
logger
.
error
(
traceback
.
format_exc
())
def
get_bbox_info
(
data
):
bbox_info
=
[]
for
page
in
data
:
preproc_blocks
=
page
.
get
(
'preproc_blocks'
,
[])
discarded_blocks
=
page
.
get
(
'discarded_blocks'
,
[])
bbox_info
.
append
({
'preproc_blocks'
:
preproc_blocks
,
'page_idx'
:
page
.
get
(
'page_idx'
),
'page_size'
:
page
.
get
(
'page_size'
),
'discarded_blocks'
:
discarded_blocks
,
})
return
bbox_info
def
analysis_pdf_task
(
pdf_dir
,
image_dir
,
pdf_path
,
is_ocr
,
analysis_pdf_id
):
"""解析pdf.
:param pdf_dir: pdf解析目录
:param image_dir: 图片目录
:param pdf_path: pdf路径
:param is_ocr: 是否启用ocr
:param analysis_pdf_id: pdf解析表id
:return:
"""
try
:
logger
.
info
(
f
'start task:
{
pdf_path
}
'
)
logger
.
info
(
f
'image_dir:
{
image_dir
}
'
)
if
not
Path
(
image_dir
).
exists
():
Path
(
image_dir
).
mkdir
(
parents
=
True
,
exist_ok
=
True
)
else
:
# 清空image_dir,避免同文件多次解析图片积累
shutil
.
rmtree
(
image_dir
,
ignore_errors
=
True
)
os
.
makedirs
(
image_dir
,
exist_ok
=
True
)
# 获取文件内容
with
open
(
pdf_path
,
'rb'
)
as
file
:
pdf_bytes
=
file
.
read
()
# 生成图片链接
with
app
.
app_context
():
image_url_prefix
=
f
"http://
{
current_app
.
config
[
'SERVER_NAME'
]
}{
current_app
.
config
[
'FILE_API'
]
}
&pdf=
{
Path
(
pdf_path
).
name
}
&filename="
# 解析文件
md_content
,
bbox_info
=
analysis_pdf
(
image_url_prefix
,
image_dir
,
pdf_bytes
,
is_ocr
)
# ############ markdown #############
pdf_name
=
Path
(
pdf_path
).
name
full_md_content
=
''
for
item
in
json
.
loads
(
md_content
):
full_md_content
+=
item
[
'md_content'
]
+
'
\n
'
full_md_name
=
'full.md'
with
open
(
f
'
{
pdf_dir
}
/
{
full_md_name
}
'
,
'w'
,
encoding
=
'utf-8'
)
as
file
:
file
.
write
(
full_md_content
)
with
app
.
app_context
():
full_md_link
=
url_for
(
'analysis.mdview'
,
filename
=
full_md_name
,
as_attachment
=
False
)
full_md_link
=
f
'
{
full_md_link
}
&pdf=
{
pdf_name
}
'
md_link_list
=
[]
with
app
.
app_context
():
for
n
,
md
in
enumerate
(
json
.
loads
(
md_content
)):
md_content
=
md
[
'md_content'
]
md_name
=
f
"
{
md
.
get
(
'page_no'
,
n
)
}
.md"
with
open
(
f
'
{
pdf_dir
}
/
{
md_name
}
'
,
'w'
,
encoding
=
'utf-8'
)
as
file
:
file
.
write
(
md_content
)
md_url
=
url_for
(
'analysis.mdview'
,
filename
=
md_name
,
as_attachment
=
False
)
md_link_list
.
append
(
f
'
{
md_url
}
&pdf=
{
pdf_name
}
'
)
with
app
.
app_context
():
with
db
.
auto_commit
():
analysis_pdf_object
=
AnalysisPdf
.
query
.
filter_by
(
id
=
analysis_pdf_id
).
first
()
analysis_pdf_object
.
status
=
1
analysis_pdf_object
.
bbox_info
=
json
.
dumps
(
bbox_info
,
ensure_ascii
=
False
)
analysis_pdf_object
.
md_link_list
=
json
.
dumps
(
md_link_list
,
ensure_ascii
=
False
)
analysis_pdf_object
.
full_md_link
=
full_md_link
db
.
session
.
add
(
analysis_pdf_object
)
with
db
.
auto_commit
():
analysis_task_object
=
AnalysisTask
.
query
.
filter_by
(
analysis_pdf_id
=
analysis_pdf_id
).
first
()
analysis_task_object
.
status
=
1
db
.
session
.
add
(
analysis_task_object
)
logger
.
info
(
'finished!'
)
except
Exception
as
e
:
# noqa: F841
logger
.
error
(
traceback
.
format_exc
())
with
app
.
app_context
():
with
db
.
auto_commit
():
analysis_pdf_object
=
AnalysisPdf
.
query
.
filter_by
(
id
=
analysis_pdf_id
).
first
()
analysis_pdf_object
.
status
=
2
db
.
session
.
add
(
analysis_pdf_object
)
with
db
.
auto_commit
():
analysis_task_object
=
AnalysisTask
.
query
.
filter_by
(
analysis_pdf_id
=
analysis_pdf_id
).
first
()
analysis_task_object
.
status
=
1
db
.
session
.
add
(
analysis_task_object
)
raise
ApiException
(
code
=
500
,
msg
=
'PDF parsing failed'
,
msgZH
=
'pdf解析失败'
)
finally
:
# 执行pending
with
app
.
app_context
():
analysis_task_object
=
AnalysisTask
.
query
.
filter_by
(
status
=
2
).
order_by
(
AnalysisTask
.
update_date
.
asc
()).
first
()
if
analysis_task_object
:
pdf_upload_folder
=
current_app
.
config
[
'PDF_UPLOAD_FOLDER'
]
upload_dir
=
f
'
{
current_app
.
static_folder
}
/
{
pdf_upload_folder
}
'
file_path
=
find_file
(
analysis_task_object
.
file_key
,
upload_dir
)
file_stem
=
Path
(
file_path
).
stem
pdf_analysis_folder
=
current_app
.
config
[
'PDF_ANALYSIS_FOLDER'
]
pdf_dir
=
f
'
{
current_app
.
static_folder
}
/
{
pdf_analysis_folder
}
/
{
file_stem
}
'
image_dir
=
f
'
{
pdf_dir
}
/images'
with
db
.
auto_commit
():
analysis_pdf_object
=
AnalysisPdf
.
query
.
filter_by
(
id
=
analysis_task_object
.
analysis_pdf_id
).
first
()
analysis_pdf_object
.
status
=
0
db
.
session
.
add
(
analysis_pdf_object
)
with
db
.
auto_commit
():
analysis_task_object
.
status
=
0
db
.
session
.
add
(
analysis_task_object
)
analysis_pdf_task
(
pdf_dir
,
image_dir
,
file_path
,
analysis_task_object
.
is_ocr
,
analysis_task_object
.
analysis_pdf_id
)
else
:
logger
.
info
(
'all task finished!'
)
projects/web_demo/web_demo/api/analysis/serialization.py
deleted
100644 → 0
View file @
3cc3f754
from
marshmallow
import
Schema
,
fields
,
validates_schema
,
validates
from
common.error_types
import
ApiException
from
.models
import
AnalysisTask
class
BooleanField
(
fields
.
Boolean
):
def
_deserialize
(
self
,
value
,
attr
,
data
,
**
kwargs
):
# 进行自定义验证
if
not
isinstance
(
value
,
bool
):
raise
ApiException
(
code
=
400
,
msg
=
"isOcr not a valid boolean"
,
msgZH
=
"isOcr不是有效的布尔值"
)
return
value
class
AnalysisViewSchema
(
Schema
):
fileKey
=
fields
.
Str
(
required
=
True
)
fileName
=
fields
.
Str
()
taskType
=
fields
.
Str
(
required
=
True
)
isOcr
=
BooleanField
()
@
validates_schema
(
pass_many
=
True
)
def
validate_passwords
(
self
,
data
,
**
kwargs
):
task_type
=
data
[
'taskType'
]
file_key
=
data
[
'fileKey'
]
if
not
file_key
:
raise
ApiException
(
code
=
400
,
msg
=
"fileKey cannot be empty"
,
msgZH
=
"fileKey不能为空"
)
if
not
task_type
:
raise
ApiException
(
code
=
400
,
msg
=
"taskType cannot be empty"
,
msgZH
=
"taskType不能为空"
)
projects/web_demo/web_demo/api/analysis/task_view.py
deleted
100644 → 0
View file @
3cc3f754
import
json
from
flask
import
url_for
,
request
from
flask_restful
import
Resource
from
sqlalchemy
import
func
from
..extentions
import
db
from
.models
import
AnalysisTask
,
AnalysisPdf
from
.ext
import
task_state_map
from
common.custom_response
import
generate_response
class
TaskView
(
Resource
):
def
get
(
self
):
"""
查询正在进行的任务
:return:
"""
analysis_task_running
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
status
==
0
).
first
()
analysis_task_pending
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
status
==
2
).
order_by
(
AnalysisTask
.
create_date
.
asc
()).
all
()
pending_total
=
db
.
session
.
query
(
func
.
count
(
AnalysisTask
.
id
)).
filter
(
AnalysisTask
.
status
==
2
).
scalar
()
if
analysis_task_running
:
task_nums
=
pending_total
+
1
file_name_split
=
analysis_task_running
.
file_name
.
split
(
"_"
)
new_file_name
=
file_name_split
[
-
1
]
if
file_name_split
else
analysis_task_running
.
file_name
data
=
[
{
"queues"
:
task_nums
,
# 正在排队的任务总数
"rank"
:
1
,
"id"
:
analysis_task_running
.
id
,
"url"
:
url_for
(
'analysis.uploadpdfview'
,
filename
=
analysis_task_running
.
file_name
,
as_attachment
=
False
),
"fileName"
:
new_file_name
,
"type"
:
analysis_task_running
.
task_type
,
"state"
:
task_state_map
.
get
(
analysis_task_running
.
status
),
}
]
else
:
task_nums
=
pending_total
data
=
[]
for
n
,
task
in
enumerate
(
analysis_task_pending
):
file_name_split
=
task
.
file_name
.
split
(
"_"
)
new_file_name
=
file_name_split
[
-
1
]
if
file_name_split
else
task
.
file_name
data
.
append
({
"queues"
:
task_nums
,
# 正在排队的任务总数
"rank"
:
n
+
2
,
"id"
:
task
.
id
,
"url"
:
url_for
(
'analysis.uploadpdfview'
,
filename
=
task
.
file_name
,
as_attachment
=
False
),
"fileName"
:
new_file_name
,
"type"
:
task
.
task_type
,
"state"
:
task_state_map
.
get
(
task
.
status
),
})
data
.
reverse
()
return
generate_response
(
data
=
data
,
total
=
task_nums
)
class
HistoricalTasksView
(
Resource
):
def
get
(
self
):
"""
获取任务历史记录
:return:
"""
params
=
request
.
args
page_no
=
params
.
get
(
'pageNo'
,
1
)
page_size
=
params
.
get
(
'pageSize'
,
10
)
total
=
db
.
session
.
query
(
func
.
count
(
AnalysisTask
.
id
)).
scalar
()
analysis_task
=
AnalysisTask
.
query
.
order_by
(
AnalysisTask
.
create_date
.
desc
()).
paginate
(
page
=
int
(
page_no
),
per_page
=
int
(
page_size
),
error_out
=
False
)
data
=
[]
for
n
,
task
in
enumerate
(
analysis_task
):
file_name_split
=
task
.
file_name
.
split
(
"_"
)
new_file_name
=
file_name_split
[
-
1
]
if
file_name_split
else
task
.
file_name
data
.
append
({
"fileName"
:
new_file_name
,
"id"
:
task
.
id
,
"type"
:
task
.
task_type
,
"state"
:
task_state_map
.
get
(
task
.
status
),
})
data
=
{
"list"
:
data
,
"total"
:
total
,
"pageNo"
:
page_no
,
"pageSize"
:
page_size
,
}
return
generate_response
(
data
=
data
)
class
DeleteTaskView
(
Resource
):
def
delete
(
self
,
id
):
"""
删除任务历史记录
:return:
"""
analysis_task
=
AnalysisTask
.
query
.
filter
(
AnalysisTask
.
id
==
id
,
AnalysisTask
.
status
!=
0
).
first
()
if
analysis_task
:
analysis_pdf
=
AnalysisPdf
.
query
.
filter
(
AnalysisPdf
.
id
==
AnalysisTask
.
analysis_pdf_id
).
first
()
with
db
.
auto_commit
():
db
.
session
.
delete
(
analysis_pdf
)
db
.
session
.
delete
(
analysis_task
)
else
:
return
generate_response
(
code
=
400
,
msg
=
"The ID is incorrect"
,
msgZH
=
"id不正确"
)
return
generate_response
(
data
=
{
"id"
:
id
})
projects/web_demo/web_demo/api/analysis/upload_view.py
deleted
100644 → 0
View file @
3cc3f754
import
json
import
time
import
traceback
import
requests
from
flask
import
request
,
current_app
,
url_for
,
send_from_directory
from
flask_restful
import
Resource
from
werkzeug.utils
import
secure_filename
from
pathlib
import
Path
from
common.ext
import
is_pdf
,
calculate_file_hash
,
url_is_pdf
from
io
import
BytesIO
from
werkzeug.datastructures
import
FileStorage
from
common.custom_response
import
generate_response
from
loguru
import
logger
class
UploadPdfView
(
Resource
):
def
get
(
self
):
"""
获取pdf
:return:
"""
params
=
request
.
args
filename
=
params
.
get
(
'filename'
)
as_attachment
=
params
.
get
(
'as_attachment'
)
if
str
(
as_attachment
).
lower
()
==
"true"
:
as_attachment
=
True
else
:
as_attachment
=
False
pdf_upload_folder
=
current_app
.
config
[
'PDF_UPLOAD_FOLDER'
]
response
=
send_from_directory
(
f
"
{
current_app
.
static_folder
}
/
{
pdf_upload_folder
}
"
,
filename
,
as_attachment
=
as_attachment
)
return
response
def
post
(
self
):
"""
上传pdf
:return:
"""
file_list
=
request
.
files
.
getlist
(
"file"
)
if
file_list
:
file
=
file_list
[
0
]
filename
=
secure_filename
(
file
.
filename
)
if
not
file
or
file
and
not
is_pdf
(
filename
,
file
):
return
generate_response
(
code
=
400
,
msg
=
"Invalid PDF file"
,
msgZH
=
"PDF文件参数无效"
)
else
:
params
=
json
.
loads
(
request
.
data
)
pdf_url
=
params
.
get
(
'pdfUrl'
)
try
:
response
=
requests
.
get
(
pdf_url
,
stream
=
True
)
except
ConnectionError
as
e
:
logger
.
error
(
traceback
.
format_exc
())
return
generate_response
(
code
=
400
,
msg
=
"params is not valid"
,
msgZh
=
"参数错误,pdf链接无法访问"
)
if
response
.
status_code
!=
200
:
return
generate_response
(
code
=
400
,
msg
=
"params is not valid"
,
msgZh
=
"参数错误,pdf链接响应状态异常"
)
# 创建一个模拟的 FileStorage 对象
file_content
=
BytesIO
(
response
.
content
)
filename
=
Path
(
pdf_url
).
name
if
".pdf"
in
pdf_url
else
f
"
{
Path
(
pdf_url
).
name
}
.pdf"
file
=
FileStorage
(
stream
=
file_content
,
filename
=
filename
,
content_type
=
response
.
headers
.
get
(
'Content-Type'
,
'application/octet-stream'
)
)
if
not
file
or
file
and
not
url_is_pdf
(
file
):
return
generate_response
(
code
=
400
,
msg
=
"Invalid PDF file"
,
msgZH
=
"PDF文件参数无效"
)
pdf_upload_folder
=
current_app
.
config
[
'PDF_UPLOAD_FOLDER'
]
upload_dir
=
f
"
{
current_app
.
static_folder
}
/
{
pdf_upload_folder
}
"
if
not
Path
(
upload_dir
).
exists
():
Path
(
upload_dir
).
mkdir
(
parents
=
True
,
exist_ok
=
True
)
file_key
=
f
"
{
calculate_file_hash
(
file
)
}{
int
(
time
.
time
())
}
"
new_filename
=
f
"
{
file_key
}
_
{
filename
}
"
file_path
=
f
"
{
upload_dir
}
/
{
new_filename
}
"
# file.save(file_path)
chunk_size
=
8192
with
open
(
file_path
,
'wb'
)
as
f
:
while
True
:
chunk
=
file
.
stream
.
read
(
chunk_size
)
if
not
chunk
:
break
f
.
write
(
chunk
)
# 生成文件的URL路径
file_url
=
url_for
(
'analysis.uploadpdfview'
,
filename
=
new_filename
,
as_attachment
=
False
)
data
=
{
"url"
:
file_url
,
"file_key"
:
file_key
}
return
generate_response
(
data
=
data
)
projects/web_demo/web_demo/api/extentions.py
deleted
100644 → 0
View file @
3cc3f754
from
contextlib
import
contextmanager
from
common.error_types
import
ApiException
from
flask
import
Flask
,
jsonify
from
flask_cors
import
CORS
from
flask_jwt_extended
import
JWTManager
from
flask_marshmallow
import
Marshmallow
from
flask_migrate
import
Migrate
from
flask_restful
import
Api
as
_Api
from
flask_sqlalchemy
import
SQLAlchemy
as
_SQLAlchemy
from
loguru
import
logger
from
werkzeug.exceptions
import
HTTPException
class
Api
(
_Api
):
def
handle_error
(
self
,
e
):
if
isinstance
(
e
,
ApiException
):
code
=
e
.
code
msg
=
e
.
msg
msgZH
=
e
.
msgZH
error_code
=
e
.
error_code
elif
isinstance
(
e
,
HTTPException
):
code
=
e
.
code
msg
=
e
.
description
msgZH
=
'服务异常,详细信息请查看日志'
error_code
=
e
.
code
else
:
code
=
500
msg
=
str
(
e
)
error_code
=
500
msgZH
=
'服务异常,详细信息请查看日志'
# 使用 loguru 记录异常信息
logger
.
opt
(
exception
=
e
).
error
(
f
'An error occurred:
{
msg
}
'
)
return
jsonify
({
'error'
:
'Internal Server Error'
if
code
==
500
else
e
.
name
,
'msg'
:
msg
,
'msgZH'
:
msgZH
,
'code'
:
code
,
'error_code'
:
error_code
}),
code
class
SQLAlchemy
(
_SQLAlchemy
):
@
contextmanager
def
auto_commit
(
self
):
try
:
yield
db
.
session
.
commit
()
db
.
session
.
flush
()
except
Exception
as
e
:
db
.
session
.
rollback
()
raise
e
app
=
Flask
(
__name__
)
CORS
(
app
,
supports_credentials
=
True
)
db
=
SQLAlchemy
()
migrate
=
Migrate
()
jwt
=
JWTManager
()
ma
=
Marshmallow
()
folder
=
app
.
config
.
get
(
'REACT_APP_DIST'
)
projects/web_demo/web_demo/api/react_app/__init__.py
deleted
100644 → 0
View file @
3cc3f754
from
pathlib
import
Path
from
flask
import
Blueprint
from
..extentions
import
app
,
Api
from
.react_app_view
import
ReactAppView
from
loguru
import
logger
folder
=
Path
(
app
.
config
.
get
(
"REACT_APP_DIST"
,
"../../web/dist/"
)).
resolve
()
logger
.
info
(
f
"react_app folder:
{
folder
}
"
)
react_app_blue
=
Blueprint
(
'react_app'
,
__name__
,
static_folder
=
folder
,
static_url_path
=
''
,
template_folder
=
folder
)
react_app_api
=
Api
(
react_app_blue
,
prefix
=
''
)
react_app_api
.
add_resource
(
ReactAppView
,
'/'
)
\ No newline at end of file
projects/web_demo/web_demo/api/react_app/react_app_view.py
deleted
100644 → 0
View file @
3cc3f754
from
flask
import
render_template
,
Response
from
flask_restful
import
Resource
class
ReactAppView
(
Resource
):
def
get
(
self
):
# 创建自定义的响应对象
rendered_template
=
render_template
(
'index.html'
)
response
=
Response
(
rendered_template
,
mimetype
=
'text/html'
)
return
response
Prev
1
…
23
24
25
26
27
28
29
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment