17 Commits

10 changed files with 2261 additions and 134 deletions

View File

@@ -4,5 +4,6 @@ DOCKER_ROOT="$DOCKER_REPO/$DOCKER_GROUP"
VERSION=$(grep -m 1 'version' ./pyproject.toml | grep -oP 'version\s*=\s*"\K[^"]+')
PYTHON_VERSION=$(grep -m 1 'python' ./pyproject.toml | grep -oP 'python\s*=\s*"\^?\K[^"]+')
DOCKER_PROJECTS=("gallery")
DOCKER_TAG=latest
OPENWEATHER_KEY="<EMPTY>"

View File

@@ -9,7 +9,7 @@ COPY pyproject.toml poetry.lock README.md ./
RUN poetry config virtualenvs.in-project true
RUN --mount=type=cache,target=/root/.cache/pypoetry/cache \
--mount=type=cache,target=/root/.cache/pypoetry/artifacts \
poetry install --with app --no-root
poetry install --extras app --no-root
COPY locales ./locales
RUN cd locales/ru/LC_MESSAGES && msgfmt messages.po

View File

@@ -1,15 +1,15 @@
name: gallery
name: gallery-${DOCKER_TAG}
services:
redis:
container_name: gallery-redis
container_name: gallery-${DOCKER_TAG}-redis
image: redis:alpine
stop_grace_period: 3s
volumes:
- redis_data:/data
app:
container_name: gallery-app
image: ${DOCKER_ROOT}/gallery
container_name: gallery-${DOCKER_TAG}-app
image: ${DOCKER_ROOT}/gallery:${DOCKER_TAG}
environment:
- REDIS_HOST=redis
- OPENWEATHER_KEY=$OPENWEATHER_KEY

View File

@@ -149,6 +149,7 @@ class WindDirectionParser(RowParser[WindDirection]):
WIND_DIRECTION_MAP: dict[str, WindDirection] = {
"": WindDirection.CALM,
"": WindDirection.CALM,
"штиль": WindDirection.CALM,
"с": WindDirection.N,
"св": WindDirection.NE,

210
poetry.lock generated
View File

@@ -192,9 +192,10 @@ frozenlist = ">=1.1.0"
name = "annotated-doc"
version = "0.0.4"
description = "Document parameters, class attributes, return types, and variables inline, with Annotated."
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"},
{file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"},
@@ -206,7 +207,7 @@ version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
groups = ["main", "app"]
groups = ["main"]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -216,9 +217,10 @@ files = [
name = "anyio"
version = "4.14.1"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72"},
{file = "anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e"},
@@ -258,9 +260,10 @@ files = [
name = "babel"
version = "2.18.0"
description = "Internationalization utilities"
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"},
{file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"},
@@ -347,9 +350,10 @@ uvloop = ["uvloop (>=0.15.2) ; sys_platform != \"win32\"", "winloop (>=0.5.0) ;
name = "certifi"
version = "2026.6.17"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
optional = true
python-versions = ">=3.7"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"},
{file = "certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432"},
@@ -361,11 +365,12 @@ version = "8.4.2"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.10"
groups = ["app", "dev"]
groups = ["main", "dev"]
files = [
{file = "click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76"},
{file = "click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6"},
]
markers = {main = "extra == \"app\""}
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
@@ -376,12 +381,12 @@ 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"
groups = ["app", "dev", "test"]
groups = ["main", "dev", "test"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
markers = {app = "platform_system == \"Windows\" or sys_platform == \"win32\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", test = "sys_platform == \"win32\""}
markers = {main = "extra == \"app\" and (platform_system == \"Windows\" or sys_platform == \"win32\")", dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", test = "sys_platform == \"win32\""}
[[package]]
name = "dateparser"
@@ -409,9 +414,10 @@ langdetect = ["langdetect (>=1.0.0)"]
name = "detect-installer"
version = "0.1.0"
description = "Detect how a Python package was installed and get the correct upgrade command"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "detect_installer-0.1.0-py3-none-any.whl", hash = "sha256:034fb20fd665c36e6ba52b8821525ea07fb4f7f938cac459df889fb33801528a"},
{file = "detect_installer-0.1.0.tar.gz", hash = "sha256:00ad7ba0a36e3cf7d08a40d3643011746dbc112597c7d475cc91c416710ca4e7"},
@@ -437,9 +443,10 @@ profile = ["gprof2dot (>=2022.7.29)"]
name = "dnspython"
version = "2.8.0"
description = "DNS toolkit"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"},
{file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"},
@@ -458,9 +465,10 @@ wmi = ["wmi (>=1.5.1) ; platform_system == \"Windows\""]
name = "email-validator"
version = "2.3.0"
description = "A robust email address syntax and deliverability validation library."
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4"},
{file = "email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426"},
@@ -474,9 +482,10 @@ idna = ">=2.0.0"
name = "fastapi"
version = "0.139.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "fastapi-0.139.0-py3-none-any.whl", hash = "sha256:cf15e1e9e667ddb0ad63811e60bd11390d1aac838ca4a7a23f421807b2308189"},
{file = "fastapi-0.139.0.tar.gz", hash = "sha256:99ab7b2d92223c76d6cf10757ab3f89d45b38267fc20b2a136cf02f6beac3145"},
@@ -507,9 +516,10 @@ standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[stand
name = "fastapi-cli"
version = "0.0.27"
description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "fastapi_cli-0.0.27-py3-none-any.whl", hash = "sha256:2e389a40f318e29fec8cb1e289f267f17c048876fb82dbfa869a10b16740495d"},
{file = "fastapi_cli-0.0.27.tar.gz", hash = "sha256:1dffb1e40c0c88f2e0171a8a252a2b615c1e63ff8c05626649e4badd6a84336a"},
@@ -530,9 +540,10 @@ standard-no-fastapi-cloud-cli = ["uvicorn[standard] (>=0.15.0)"]
name = "fastapi-cloud-cli"
version = "0.22.0"
description = "Deploy and manage FastAPI Cloud apps from the command line 🚀"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "fastapi_cloud_cli-0.22.0-py3-none-any.whl", hash = "sha256:799e263140d1b6e679390fc6e90da404556aee6730fd479d3b52475e850073bf"},
{file = "fastapi_cloud_cli-0.22.0.tar.gz", hash = "sha256:cde14470227fc9275075ff537977d2991e8ce1baf947055d7a4149726de27e08"},
@@ -556,9 +567,10 @@ standard = ["uvicorn[standard] (>=0.15.0)"]
name = "fastar"
version = "0.11.0"
description = "High-level bindings for the Rust tar crate"
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "fastar-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7c906ad371ca365591ebcb7630009923f3eceb20956814494d15591a78e9e46"},
{file = "fastar-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6919497b35fa5bd978d2c26ee117cf1771b90ee5073f7518e44b9bc364b57715"},
@@ -845,9 +857,10 @@ files = [
name = "h11"
version = "0.16.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"},
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
@@ -857,9 +870,10 @@ files = [
name = "httpcore"
version = "1.0.9"
description = "A minimal low-level HTTP client."
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"},
{file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"},
@@ -879,9 +893,10 @@ trio = ["trio (>=0.22.0,<1.0)"]
name = "httptools"
version = "0.8.0"
description = "A collection of framework independent HTTP protocol utils."
optional = false
optional = true
python-versions = ">=3.9"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "httptools-0.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bf3b6f807c8541503cecfbb8a8dffb385640d0d96102f3d112aa8740f9b7c826"},
{file = "httptools-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da684f2e1aa2ee9bdcb083f3f3a68c5956750b375bc5df864d3a5f0c42a40b77"},
@@ -939,9 +954,10 @@ files = [
name = "httpx"
version = "0.28.1"
description = "The next generation HTTP client."
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
@@ -966,7 +982,7 @@ version = "3.18"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.9"
groups = ["main", "app"]
groups = ["main"]
files = [
{file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"},
{file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"},
@@ -1006,9 +1022,10 @@ colors = ["colorama"]
name = "jinja2"
version = "3.1.6"
description = "A very fast and expressive template engine."
optional = false
optional = true
python-versions = ">=3.7"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
@@ -1024,9 +1041,10 @@ i18n = ["Babel (>=2.7)"]
name = "markdown-it-py"
version = "4.2.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a"},
{file = "markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49"},
@@ -1048,9 +1066,10 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "pytest-tim
name = "markupsafe"
version = "3.0.3"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
optional = true
python-versions = ">=3.9"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"},
{file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"},
@@ -1159,9 +1178,10 @@ files = [
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
optional = true
python-versions = ">=3.7"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
@@ -1529,7 +1549,7 @@ version = "2.13.4"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main", "app"]
groups = ["main"]
files = [
{file = "pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba"},
{file = "pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6"},
@@ -1552,7 +1572,7 @@ version = "2.46.4"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
groups = ["main", "app"]
groups = ["main"]
files = [
{file = "pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4"},
{file = "pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5"},
@@ -1683,9 +1703,10 @@ typing-extensions = ">=4.14.1"
name = "pydantic-extra-types"
version = "2.11.1"
description = "Extra Pydantic types."
optional = false
optional = true
python-versions = ">=3.9"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "pydantic_extra_types-2.11.1-py3-none-any.whl", hash = "sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1"},
{file = "pydantic_extra_types-2.11.1.tar.gz", hash = "sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049"},
@@ -1709,9 +1730,10 @@ uuid-utils = ["uuid-utils (>=0.6.0) ; python_version < \"3.14\""]
name = "pydantic-settings"
version = "2.14.2"
description = "Settings management using Pydantic"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440"},
{file = "pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f"},
@@ -1735,11 +1757,12 @@ version = "2.20.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.9"
groups = ["app", "test"]
groups = ["main", "test"]
files = [
{file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"},
{file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"},
]
markers = {main = "extra == \"app\""}
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
@@ -1829,9 +1852,10 @@ six = ">=1.5"
name = "python-dotenv"
version = "1.2.2"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"},
{file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"},
@@ -1844,9 +1868,10 @@ cli = ["click (>=5.0)"]
name = "python-multipart"
version = "0.0.32"
description = "A streaming multipart parser for Python"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23"},
{file = "python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e"},
@@ -1923,9 +1948,10 @@ files = [
name = "pyyaml"
version = "6.0.3"
description = "YAML parser and emitter for Python"
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"},
{file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"},
@@ -2150,9 +2176,10 @@ files = [
name = "rich"
version = "15.0.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
optional = true
python-versions = ">=3.9.0"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb"},
{file = "rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36"},
@@ -2169,9 +2196,10 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
name = "rich-toolkit"
version = "0.20.1"
description = "Rich toolkit for building command-line applications"
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "rich_toolkit-0.20.1-py3-none-any.whl", hash = "sha256:2a6d5f8e15759b9eba5a9ee63da10b275359ead20e5a0fc92bd5b4dbae8ce4bf"},
{file = "rich_toolkit-0.20.1.tar.gz", hash = "sha256:c7336ae281f435c785acecaedc4b71d4b663dc73d9c8079fea96372527e822a4"},
@@ -2186,9 +2214,10 @@ typing-extensions = ">=4.12.2"
name = "rignore"
version = "0.7.6"
description = "Python Bindings for the ignore crate"
optional = false
optional = true
python-versions = ">=3.8"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45"},
{file = "rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec"},
@@ -2350,9 +2379,10 @@ files = [
name = "sentry-sdk"
version = "2.64.0"
description = "Python client for Sentry (https://sentry.io)"
optional = false
optional = true
python-versions = ">=3.6"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "sentry_sdk-2.64.0-py3-none-any.whl", hash = "sha256:715ea91ca860a819e8d8a50a7bde3a80d0df3b4ed7b6660a20fb9a2d084188f1"},
{file = "sentry_sdk-2.64.0.tar.gz", hash = "sha256:68be2c29e14ae310f8a39e1a79916b6d85c6cb41dcce789d14ff05fe293e4c55"},
@@ -2414,9 +2444,10 @@ unleash = ["UnleashClient (>=6.0.1)"]
name = "shellingham"
version = "1.5.4"
description = "Tool to Detect Surrounding Shell"
optional = false
optional = true
python-versions = ">=3.7"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
@@ -2450,9 +2481,10 @@ files = [
name = "starlette"
version = "1.3.1"
description = "The little ASGI library that shines."
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6"},
{file = "starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0"},
@@ -2480,9 +2512,10 @@ files = [
name = "typer"
version = "0.26.8"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "typer-0.26.8-py3-none-any.whl", hash = "sha256:3512ca79ac5c11113414b36e80281b872884477722440691c89d1112e321a49c"},
{file = "typer-0.26.8.tar.gz", hash = "sha256:c244a6bd558886fe3f8780efb6bdd28bb9aff005a94eedebaa5cb32926fe2f7e"},
@@ -2500,7 +2533,7 @@ version = "4.15.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
groups = ["main", "app"]
groups = ["main"]
files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
@@ -2512,7 +2545,7 @@ version = "0.4.2"
description = "Runtime typing introspection tools"
optional = false
python-versions = ">=3.9"
groups = ["main", "app"]
groups = ["main"]
files = [
{file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"},
{file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"},
@@ -2557,9 +2590,10 @@ testing = ["check_manifest", "pyroma", "pytest (>=4.3)", "pytest-cov", "pytest-m
name = "urllib3"
version = "2.7.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"},
{file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"},
@@ -2575,9 +2609,10 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
name = "uvicorn"
version = "0.49.0"
description = "The lightning-fast ASGI server."
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f"},
{file = "uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3"},
@@ -2601,10 +2636,10 @@ standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.8.0)
name = "uvloop"
version = "0.22.1"
description = "Fast implementation of asyncio event loop on top of libuv"
optional = false
optional = true
python-versions = ">=3.8.1"
groups = ["app"]
markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\""
groups = ["main"]
markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"app\""
files = [
{file = "uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c"},
{file = "uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792"},
@@ -2666,9 +2701,10 @@ test = ["aiohttp (>=3.10.5)", "flake8 (>=6.1,<7.0)", "mypy (>=0.800)", "psutil",
name = "watchfiles"
version = "1.2.0"
description = "Simple, modern and high performance file watching and code reload in python."
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "watchfiles-1.2.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bb68bf4df85abebe5efddc53cf2075520f243a59868d9b3973278b23e76962a9"},
{file = "watchfiles-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c16cb06dd17d43b9d185094268459eac92c9538356f050e55b54e82cf700e1d4"},
@@ -2786,9 +2822,10 @@ anyio = ">=3.0.0"
name = "websockets"
version = "16.0"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false
optional = true
python-versions = ">=3.10"
groups = ["app"]
groups = ["main"]
markers = "extra == \"app\""
files = [
{file = "websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a"},
{file = "websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0"},
@@ -2972,7 +3009,10 @@ idna = ">=2.0"
multidict = ">=4.0"
propcache = ">=0.2.1"
[extras]
app = ["babel", "fastapi", "jinja2"]
[metadata]
lock-version = "2.1"
python-versions = "^3.14"
content-hash = "16dac910366fb78d67f45bac4c8cf19c0f448cb53fccb7e33c13ea22d509783f"
python-versions = ">=3.14,<4.0"
content-hash = "7f8a3d007747f68e9735e957a0c06459044c8006b89cf3f5d37512f36638abd5"

View File

@@ -1,40 +1,40 @@
[tool.poetry]
[project]
name = "gallery"
version = "0.5.2"
description = ""
authors = ["shmyga <shmyga.z@gmail.com>"]
version = "0.5.2"
readme = "README.md"
authors = [{ name = "shmyga", email = "shmyga.z@gmail.com" }]
requires-python = '>=3.14,<4.0'
dependencies = [
'aiohttp (>=3.14,<4.0)',
'beautifulsoup4 (>=4.15,<5.0)',
'dateparser (>=1.4,<2.0)',
'pydantic (>=2.13,<3.0)',
'aiocache[redis] (>=0.12,<0.13)',
]
[project.optional-dependencies]
app = [
"fastapi[standard] (>=0.139,<0.140)",
"jinja2 (>=3.1,<4.0)",
"babel (>=2.18,<3.0)",
]
[dependency-groups]
test = ["pytest (>=9.1,<10.0)", "pytest-asyncio (>=1.4,<2.0)"]
dev = ["pylint (>=4.0.6,<5.0.0)", "black (>=26.5,<27.0)", "isort (>=8.0,<9.0)"]
[project.scripts]
gallery = "gallery.main:run"
[tool.poetry]
packages = [{ include = "gallery" }]
[tool.poetry.dependencies]
python = "^3.14"
aiohttp = "^3.14"
beautifulsoup4 = "^4.15"
dateparser = "^1.4"
pydantic = "^2.13"
aiocache = { extras = ["redis"], version = "^0.12" }
[tool.poetry.group.app.dependencies]
fastapi = {extras = ["standard"], version = "^0.139.0"}
jinja2 = "^3.1"
babel = "^2.18"
[tool.poetry.group.test.dependencies]
pytest = "^9.1"
pytest-asyncio = "^1.4"
[tool.poetry.group.dev.dependencies]
pylint = "^4.0"
black = "^26.5"
isort = "^8.0"
requires-poetry = '>=2.0.0,<3.0.0'
[build-system]
requires = ["poetry-core"]
requires = ['poetry-core (>=2.0.0,<3.0.0)']
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
gallery = "gallery.main:run"
[tool.black]
line-length = 120

View File

@@ -29,9 +29,9 @@ publish () {
for PROJECT in "${DOCKER_PROJECTS[@]}"; do
IFS=: read -r PROJECT_NAME PROJECT_TARGET <<< "$PROJECT"
docker tag $DOCKER_GROUP/$PROJECT_NAME $DOCKER_ROOT/$PROJECT_NAME:$VERSION
docker tag $DOCKER_GROUP/$PROJECT_NAME $DOCKER_ROOT/$PROJECT_NAME:latest
docker tag $DOCKER_GROUP/$PROJECT_NAME $DOCKER_ROOT/$PROJECT_NAME:$DOCKER_TAG
docker push $DOCKER_ROOT/$PROJECT_NAME:$VERSION
docker push $DOCKER_ROOT/$PROJECT_NAME:latest
docker push $DOCKER_ROOT/$PROJECT_NAME:$DOCKER_TAG
done
}

2089
static/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,12 +16,18 @@
"flag-icons": "^7.5.0"
},
"devDependencies": {
"@babel/core": "^8.0.1",
"@babel/preset-env": "^8.0.2",
"@types/bootstrap": "^5.2.11",
"@types/node": "^26.1.0",
"babel-loader": "^10.1.1",
"copy-webpack-plugin": "^14.0.0",
"core-js": "^3.49.0",
"css-loader": "^7.1.4",
"file-loader": "^6.2.0",
"mini-css-extract-plugin": "^2.10.2",
"postcss": "^8.5.22",
"postcss-loader": "^8.2.1",
"sass": "^1.101.0",
"sass-loader": "^17.0.0",
"style-loader": "^4.0.0",
@@ -30,5 +36,9 @@
"typescript": "^6.0.3",
"webpack": "^5.108.3",
"webpack-cli": "^7.1.0"
}
},
"browserslist": [
"> 0.5%",
"last 2 versions"
]
}

View File

@@ -9,26 +9,26 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const config: webpack.Configuration = {
target: ["web", "es6"],
entry: ["./src/index.ts", "./src/main.scss"],
module: {
rules: [
{ test: /\.svg$/, use: ["file-loader"] },
{
test: /\.tsx?$/,
use: {
loader: "ts-loader",
},
use: [{ loader: "babel-loader" }, { loader: "ts-loader" }],
exclude: /node_modules/,
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: true, url: false } },
{ loader: "css-loader", options: { sourceMap: false, url: false } },
{ loader: "postcss-loader", options: { sourceMap: false } },
{
loader: "sass-loader",
options: {
sourceMap: true,
sourceMap: false,
sassOptions: { quietDeps: true, silenceDeprecations: ["color-functions", "global-builtin", "import"] },
},
},