32 Commits

Author SHA1 Message Date
a91ec12d22 ci(version): 0.3.3 2026-06-22 15:33:36 +03:00
9c862863e5 fix(gismeteo): fix precipitation parser 2026-06-22 15:33:19 +03:00
fc59e52337 ci(version): 0.3.2 2026-06-20 18:59:43 +03:00
3e93a41400 fix(gismeteo): fix date-time row parsing 2026-06-20 18:59:27 +03:00
c2cd18386b refactor(easel): update api router 2026-06-16 21:18:16 +03:00
2bca3dd75a ci(version): 0.3.1 2026-06-16 20:17:37 +03:00
469bd9bc1f feat(easel): add version to header 2026-06-16 20:17:12 +03:00
027d1e2d55 build(docker): add docker build caches mount 2026-06-16 20:01:29 +03:00
7cf0012229 feat(schedule): update navigate icons 2026-06-16 20:00:46 +03:00
edc014d98c docs: update screenshot 2026-06-15 23:37:31 +03:00
1813ec213b ci(version): 0.3.0 2026-06-15 23:19:57 +03:00
1b700086f2 feat(weather): update navigate icons 2026-06-15 23:18:48 +03:00
02a6ffc931 feat(weather): new weather icons 2026-06-15 11:22:41 +03:00
2dfedfea57 fix(easel): fix header icons 2026-06-15 11:22:28 +03:00
8012d9b8ed fix(gismeteo): add max_date validation 2026-06-14 10:23:38 +03:00
3a6faa85be fix(gismeteo): add new variants to sky parser 2026-06-14 10:23:13 +03:00
1af61aa3c7 feat(easel): add sky details tooltip 2026-06-14 10:22:43 +03:00
315838604e feat(easel): add widget mode support 2026-06-14 09:48:27 +03:00
f368e6717c style: format code 2026-06-12 00:16:15 +03:00
91e2c9d123 ci(version): 0.2.3 2026-06-10 11:49:56 +03:00
91d9c37612 build(vscode): add settings 2026-06-10 11:49:48 +03:00
b5f2c272bb feat(easel): update navbar 2026-06-10 11:37:49 +03:00
eec72c77ab docs: add screenshot 2026-05-07 01:11:42 +03:00
160ec2b48b ci(version): 0.2.2 2026-04-24 14:12:39 +03:00
7c57f939c0 feat(static): update and optimizate 2026-04-24 14:12:14 +03:00
c233b020fc ci(version): 0.2.1 2026-04-23 21:24:28 +03:00
869a8ae79f fix(gismeteo): fix optional subdistrict in gismeteo reponse 2026-04-23 21:24:13 +03:00
4c3b3aeafc ci(version): 0.2.0 2026-04-23 21:06:15 +03:00
d1592150fd build(scipts): add version script 2026-04-23 21:06:06 +03:00
9351b9f53a feat(easel): add localization 2026-04-23 15:47:16 +03:00
ecb574e286 feat(easel): add bootstrap 2026-04-22 21:42:39 +03:00
94870a5c86 feat(weather): add weather location search 2026-04-22 12:58:56 +03:00
136 changed files with 16990 additions and 13485 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,7 @@
*.pyc *.pyc
*.mo
.pytest_cache .pytest_cache
.venv .venv
#.vscode #.vscode
static/node_modules
static/dist

9
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"python-envs.pythonProjects": [
{
"path": ".",
"envManager": "ms-python.python:poetry",
"packageManager": "ms-python.python:poetry"
}
]
}

View File

@@ -1,23 +1,35 @@
FROM python:3.12 AS builder FROM python:3.12 AS builder
ENV POETRY_HOME="/opt/poetry" ENV POETRY_HOME="/opt/poetry"
ENV PATH="$POETRY_HOME/bin:$PATH" ENV PATH="$POETRY_HOME/bin:$PATH"
RUN apt update && \
apt install -y gettext
WORKDIR /app WORKDIR /app
RUN curl -sSL https://install.python-poetry.org | python3 - RUN curl -sSL https://install.python-poetry.org | python3 -
COPY pyproject.toml poetry.lock README.md ./ COPY pyproject.toml poetry.lock README.md ./
RUN poetry config virtualenvs.in-project true RUN poetry config virtualenvs.in-project true
RUN poetry install --with app --no-root RUN --mount=type=cache,target=/root/.cache/pypoetry/cache \
--mount=type=cache,target=/root/.cache/pypoetry/artifacts \
poetry install --with app --no-root
COPY locales ./locales
RUN cd locales/ru/LC_MESSAGES && msgfmt messages.po
FROM node:24 AS node-builder
ENV PATH=/app/node_modules/.bin:$PATH
WORKDIR /app
COPY static/package.json static/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY static ./
RUN npm run build
FROM python:3.12-slim FROM python:3.12-slim
ENV PATH="/app/.venv/bin:$PATH" ENV PATH="/app/.venv/bin:$PATH"
WORKDIR /app WORKDIR /app
RUN apt update && \
apt install -y locales && \
sed -i -e 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/' /etc/locale.gen && \
dpkg-reconfigure --frontend=noninteractive locales
ENV LANG=ru_RU.UTF-8
ENV LC_ALL=ru_RU.UTF-8
ENV TZ="Europe/Moscow" ENV TZ="Europe/Moscow"
COPY --from=builder /app ./ COPY --from=builder /app ./
COPY --from=node-builder /app/dist ./static/dist
COPY gallery gallery/ COPY gallery gallery/
COPY --from=builder --parents locales/**/*.mo ./
CMD ["uvicorn", "gallery.main:app", "--host", "0.0.0.0", "--port", "80", "--log-config", "gallery/logging.yaml"] CMD ["uvicorn", "gallery.main:app", "--host", "0.0.0.0", "--port", "80", "--log-config", "gallery/logging.yaml"]

View File

@@ -1,9 +1,13 @@
# Gallery # API Gallery
Weather and TV program API
![API Gallery](docs/screenshot.png "API Gallery")
## View ## View
https://api.shmyga.ru https://api.shmyga.ru
## API Docs ## Swagger
https://api.shmyga.ru/docs https://api.shmyga.ru/docs

View File

@@ -1,2 +0,0 @@
#!/usr/bin/env bash
docker compose -f docker-compose-develop.yaml up --build --watch

BIN
docs/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@@ -33,12 +33,17 @@
"type": "debugpy", "type": "debugpy",
"request": "launch", "request": "launch",
"module": "uvicorn", "module": "uvicorn",
"args": [ "args": ["gallery.main:app", "--reload", "--log-config", "gallery/logging.yaml"],
"gallery.main:app", "justMyCode": true,
"--reload", "consoleTitle": "gallery:app",
"--log-config", },
"gallery/logging.yaml", {
], "name": "gallery:static",
"cwd": "${workspaceFolder}/static",
"request": "launch",
"type": "node-terminal",
"command": "npm run dev",
"consoleTitle": "gallery:static",
}, },
], ],
}, },

View File

@@ -1,23 +1,21 @@
import locale as _locale
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from gallery.sketch.bundle import ApiBundle from gallery.sketch.bundle import ApiBundle
from gallery.util import root_path
from .route import api, doc, view from .route import api, doc, view
DEFAULT_LOCALE = "ru_RU.UTF-8"
def build_app(api_bundle: ApiBundle) -> FastAPI:
def build_app(api_bundle: ApiBundle, *, locale: str = DEFAULT_LOCALE) -> FastAPI:
_locale.setlocale(_locale.LC_TIME, locale)
app = FastAPI( app = FastAPI(
title="Gallery", title="Gallery",
docs_url=None, docs_url=None,
redoc_url=None, redoc_url=None,
) )
app.state.api = api_bundle app.state.api = api_bundle
app.mount("/static", StaticFiles(directory=root_path / "static/dist"))
doc.mount(app) doc.mount(app)
api.mount(app) app.include_router(api.router)
view.mount(app) app.include_router(view.router)
return app return app

View File

@@ -1,8 +1,7 @@
from fastapi import FastAPI from fastapi import APIRouter
from . import schedule, weather from . import schedule, weather
router = APIRouter(prefix="/api", tags=["API"])
def mount(app: FastAPI): router.include_router(weather.router)
weather.mount(app) router.include_router(schedule.router)
schedule.mount(app)

View File

@@ -1,20 +1,20 @@
import datetime import datetime
from fastapi import FastAPI from fastapi import APIRouter
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.sketch.schedule.model import ChannelId, Schedule from gallery.sketch.schedule.model import ChannelId, Schedule
router = APIRouter(prefix="/schedule")
def mount(app: FastAPI):
@app.get("/api/schedule/channels") @router.get("/channels")
async def get_api_schedule_channels(request: AppRequest) -> list[ChannelId]: async def get_api_schedule_channels(request: AppRequest) -> list[ChannelId]:
schedule_api = request.app.state.api.schedule schedule_api = request.app.state.api.schedule
return await schedule_api.get_channels() return await schedule_api.get_channels()
@app.get("/api/schedule/{channel}/{date}")
async def get_api_schedule_channel_schedule( @router.get("/{channel}/{date}")
request: AppRequest, channel: str, date: datetime.date async def get_api_schedule_channel_schedule(request: AppRequest, channel: str, date: datetime.date) -> Schedule:
) -> Schedule:
schedule_api = request.app.state.api.schedule schedule_api = request.app.state.api.schedule
return await schedule_api.get_channel_schedule(ChannelId(channel), date) return await schedule_api.get_channel_schedule(ChannelId(channel), date)

View File

@@ -1,27 +1,26 @@
import datetime import datetime
from fastapi import FastAPI from fastapi import APIRouter
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.sketch.weather.model import WeatherResponse from gallery.sketch.weather.model import Location, WeatherResponse
router = APIRouter(prefix="/weather")
def mount(app: FastAPI): @router.get("/locations")
@app.get("/api/weather/locations") async def get_api_weather_locations(request: AppRequest, query: str) -> list[Location]:
async def get_api_weather_locations(request: AppRequest) -> list[str]:
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
return await weather_api.get_locations() return await weather_api.find_locations(query)
@app.get("/api/weather/{location}/day/{date}")
async def get_api_weather_day( @router.get("/{location}/day/{date}")
request: AppRequest, location: str, date: datetime.date async def get_api_weather_day(request: AppRequest, location: str, date: datetime.date) -> WeatherResponse:
) -> WeatherResponse:
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
return await weather_api.get_day(location, date) return await weather_api.get_day(location, date)
@app.get("/api/weather/{location}/days/{days}")
async def get_api_weather_days( @router.get("/{location}/days/{days}")
request: AppRequest, location: str, days: int async def get_api_weather_days(request: AppRequest, location: str, days: int) -> WeatherResponse:
) -> WeatherResponse:
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
return await weather_api.get_days(location, days) return await weather_api.get_days(location, days)

View File

@@ -1,9 +1,11 @@
from fastapi import FastAPI from fastapi import APIRouter, Depends
from . import common, schedule, weather from .common import router as common_router
from .schedule import router as schedule_router
from .translation import set_language
from .weather import router as weather_router
router = APIRouter(tags=["view"], dependencies=[Depends(set_language)])
def mount(app: FastAPI): router.include_router(common_router)
common.mount(app) router.include_router(weather_router)
weather.mount(app) router.include_router(schedule_router)
schedule.mount(app)

View File

@@ -1,37 +1,36 @@
from pathlib import Path from pathlib import Path
from typing import NamedTuple from typing import NamedTuple
from fastapi import FastAPI, Request from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from gallery.version import __version__ from ..common.utils.template import build_templates
class Section(NamedTuple): class Section(NamedTuple):
link: str link: str
title: str title: str
icon: str
SECTIONS = [ SECTIONS = [
Section("weather", "Погода"), Section("weather", "Weather", "brightness-high"),
Section("schedule", "Телепрограмма"), Section("schedule", "TV program", "tv"),
] ]
base_dir = Path(__file__).parent
def mount(app: FastAPI): router = APIRouter()
base_dir = Path(__file__).parent
app.mount("/static/common", StaticFiles(directory=base_dir / "static"))
templates = Jinja2Templates(directory=base_dir / "templates")
@app.get("/", response_class=HTMLResponse) templates = build_templates()
async def get_section_list(request: Request):
@router.get("/", response_class=HTMLResponse)
async def get_section_list(request: Request):
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="root_index.html", name="root_index.html",
context={ context={
"version": __version__,
"sections": SECTIONS, "sections": SECTIONS,
}, },
) )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,114 +0,0 @@
/*
base
*/
body {
font-size: 1.5rem;
}
h3 {
margin: 0.5rem 0;
}
/*
table
*/
table {
table-layout: fixed;
border-collapse: collapse;
}
table,
th,
td {
text-align: center;
}
td {
padding: 0.1rem 0.4rem;
}
/*
a.button
*/
a.button {
text-decoration: none;
color: inherit;
}
.button.disabled {
pointer-events: none;
cursor: default;
color: gray;
filter: grayscale(100%);
}
/*
app
*/
.app-container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: stretch;
}
.app-menu {
display: flex;
flex-direction: column;
margin: 0.5rem;
}
.app-content {
display: flex;
flex: 1;
flex-direction: column;
}
.app-header {
width: 100%;
display: flex;
flex-direction: row;
gap: 0.5rem;
justify-content: center;
}
.app-link-home > * {
width: 2rem;
height: 2rem;
background-image: url("/static/common/gallery.png");
background-size: contain;
}
.icon {
display: inline-block;
width: 2rem;
height: 2rem;
background-size: contain;
}
ul.app-list {
list-style: none;
padding-left: 0;
width: 30rem;
margin: auto;
}
ul.app-list > li {
border: 1px solid lightgrey;
}
ul.app-list > li > a {
display: flex;
gap: 0.25rem;
padding: 0.5rem 2rem;
text-decoration: none;
color: inherit;
}
ul.app-list > li:hover {
border-color: rgb(125, 125, 255);
}
ul.app-list > li:hover > a {
color: rgb(125, 125, 255);
}

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{request.state.language}}">
<head> <head>
{% block head %} {% block head %}
@@ -10,28 +10,115 @@
content="ie=edge"> content="ie=edge">
<title>{% block title %}{% endblock %}</title> <title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" <link rel="stylesheet"
href="/static/common/style.css?v={{version}}"> href="/static/gallery.css?v={{version}}">
<script type="module"
src="/static/gallery.es.js?v={{version}}"></script>
<link rel="icon" <link rel="icon"
href="/static/common/favicon.ico?v={{version}}" href="/favicon.ico?v={{version}}"
type="image/x-icon"> type="image/x-icon">
{% endblock %} {% endblock %}
</head> </head>
<body class="app-container"> <body class="{{ is_widget and 'widget' or ''}}">
<div class="app-menu"> <div class="app col-lg-8 mx-auto p-3 py-md-5">
<a class="app-link-home" {% if not is_widget %}
href="/"> <header class="app-header pb-3 mb-5 border-bottom">
<div></div> <div>{{request.query_params.widget}}</div>
</a> <div class="link-list">
<app-link href="/"
icon="gear">API Gallery</app-link>
{% block header %}{% endblock %}
</div> </div>
<div class="app-content"> <ul class="navbar-nav flex-row flex-wrap ms-md-auto">
<h3 class="app-header"> <li class="nav-item me-2">
{% block header %}{% endblock %}</span> <span class="nav-link">{{ version }}</span>
</h3> </li>
<li class="nav-item dropdown">
<button class="btn btn-link nav-link py-2 px-0 px-lg-2 dropdown-toggle d-flex align-items-center"
id="bd-language"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="{{_('Select language')}} (default)">
<span class="fi fir fi-gb me-2 language-icon-active icon-header"></span>
<span class="d-lg-none ms-2"
id="bd-language-text">{{_("Select language")}}</span>
</button>
<ul class="dropdown-menu dropdown-menu-end"
aria-labelledby="bd-language-text">
<li>
<button type="button"
class="dropdown-item d-flex align-items-center"
data-bs-language-value="en"
aria-pressed="false">
<span class="fi fir fi-gb me-2 language-icon-active"></span>
{{_("English")}}
</button>
</li>
<li>
<button type="button"
class="dropdown-item d-flex align-items-center"
data-bs-language-value="ru"
aria-pressed="false">
<span class="fi fir fi-ru me-2 language-icon-active"></span>
{{_("Russian")}}
</button>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<button class="btn btn-link nav-link py-2 px-0 px-lg-2 dropdown-toggle d-flex align-items-center"
id="bd-theme"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="Toggle theme (auto)">
<span class="bi bi-circle-half me-2 opacity-50 theme-icon-active icon-header"></span>
<span class="d-lg-none ms-2"
id="bd-theme-text">{{_("Toggle theme")}}</span>
</button>
<ul class="dropdown-menu dropdown-menu-end"
aria-labelledby="bd-theme-text">
<li>
<button type="button"
class="dropdown-item d-flex align-items-center"
data-bs-theme-value="light"
aria-pressed="false">
<span class="bi bi-sun-fill me-2 opacity-50 theme-icon"></span>
{{_("Light")}}
</button>
</li>
<li>
<button type="button"
class="dropdown-item d-flex align-items-center"
data-bs-theme-value="dark"
aria-pressed="false">
<span class="bi bi-moon-stars-fill me-2 opacity-50 theme-icon"></span>
{{_("Dark")}}
</button>
</li>
<li>
<button type="button"
class="dropdown-item d-flex align-items-center active"
data-bs-theme-value="auto"
aria-pressed="true">
<span class="bi bi-circle-half me-2 opacity-50 theme-icon"></span>
{{_("Auto")}}
</button>
</li>
</ul>
</li>
</ul>
</header>
{% endif %}
<main>
{% block content %}{% endblock %} {% block content %}{% endblock %}
<div class="app-footer"> </main>
{% block footer %}{% endblock %} {% if not is_widget %}
</div> <footer class="pt-5 my-5 text-muted border-top">
Created by shmyga &middot; &copy; 2026
</footer>
{% endif %}
</div> </div>
</body> </body>

View File

@@ -1,21 +1,26 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Информация{% endblock %} {% block title %}{{_("Index")}}{% endblock %}
{% block head %} {% block head %}
{{ super() }} {{ super() }}
{% endblock %} {% endblock %}
{% block header %}Информация{% endblock %}
{% block content %} {% block content %}
<ul class="app-list"> <h1>{{_("View")}}</h1>
<div class="list-group mb-5">
{% for section in sections %} {% for section in sections %}
<li> <a href="{{section.link}}"
<a href="{{section.link}}"> class="list-group-item list-group-item-action px-4">
<span class="icon" <app-link href="{{section.link}}"
style="background-image: url(/static/{{section.link}}/{{section.link}}.png);"></span> icon="{{section.icon}}">
<span>{{section.title}}</span> {{_(section.title)}}
</app-link>
</a> </a>
</li>
{% endfor %} {% endfor %}
</ul> </div>
<hr class="col-3 col-md-2 mb-5">
<h1>{{_("Docs")}}</h1>
<a href="/docs"
target="_blank">
<h4>Swagger</h4>
</a>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,43 @@
import datetime
from pathlib import Path
from babel.dates import format_date
from fastapi import Request
from fastapi.templating import Jinja2Templates
from gallery.version import __version__
from ...translation import _
from .tag import TagUtil
def is_widget(request: Request) -> bool:
return (request.url.hostname and request.url.hostname.startswith("weather")) or (
request.query_params.get("widget") is not None
)
def context_processor(request: Request) -> dict:
return {
"is_widget": is_widget(request),
}
def build_templates(templates_dir: Path | None = None, filters: dict | None = None) -> Jinja2Templates:
directory = [Path(__file__).parent.parent / "templates"]
if templates_dir:
directory.append(templates_dir)
templates = Jinja2Templates(directory=directory, context_processors=[context_processor])
templates.env.globals.update(
{
"_": _,
"version": __version__,
"format_date": format_date,
"datetime": datetime,
"tag_util": TagUtil,
"DATE_FORMAT": "E, d MMMM Y",
}
)
if filters:
templates.env.filters.update(filters)
return templates

View File

@@ -1,33 +1,28 @@
import asyncio
import datetime import datetime
from pathlib import Path from pathlib import Path
from fastapi import FastAPI from fastapi import APIRouter
from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.sketch.schedule.catalog import BUNDLE from gallery.sketch.schedule.catalog import BUNDLE
from gallery.version import __version__
from ..common.util import TagType, TagUtil from ..common.utils.tag import TagType, TagUtil
from ..common.utils.template import build_templates
from .filters import timedelta_format from .filters import timedelta_format
templates = build_templates(
Path(__file__).parent / "templates",
{
"timedelta_format": timedelta_format,
},
)
def mount(app: FastAPI): router = APIRouter()
base_dir = Path(__file__).parent
app.mount("/static/schedule", StaticFiles(directory=base_dir / "static"))
templates = Jinja2Templates(
directory=[
base_dir.parent / "common/templates",
base_dir / "templates",
]
)
templates.env.filters["timedelta_format"] = timedelta_format
@app.get("/schedule", response_class=HTMLResponse)
async def get_schedule_list(request: AppRequest): @router.get("/schedule", response_class=HTMLResponse)
async def get_schedule_list(request: AppRequest):
schedule_api = request.app.state.api.schedule schedule_api = request.app.state.api.schedule
channels = await schedule_api.get_channels() channels = await schedule_api.get_channels()
channels_data = BUNDLE.select_items(channels) channels_data = BUNDLE.select_items(channels)
@@ -35,13 +30,13 @@ def mount(app: FastAPI):
request=request, request=request,
name="index.html", name="index.html",
context={ context={
"version": __version__,
"channels": channels_data, "channels": channels_data,
}, },
) )
@app.get("/schedule/tag/{tag}", response_class=HTMLResponse)
async def get_schedule_tag(request: AppRequest, tag: str, live: bool = False): @router.get("/schedule/tag/{tag}", response_class=HTMLResponse)
async def get_schedule_tag(request: AppRequest, tag: str, live: bool = False):
tag_value = TagUtil.parse_tag(tag) tag_value = TagUtil.parse_tag(tag)
schedule_api = request.app.state.api.schedule schedule_api = request.app.state.api.schedule
results = await schedule_api.get_all_schedules(tag_value.date) results = await schedule_api.get_all_schedules(tag_value.date)
@@ -49,7 +44,6 @@ def mount(app: FastAPI):
request=request, request=request,
name="schedule.html", name="schedule.html",
context={ context={
"version": __version__,
"tag_util": TagUtil, "tag_util": TagUtil,
"datetime": datetime, "datetime": datetime,
"response": results[0], "response": results[0],
@@ -58,12 +52,14 @@ def mount(app: FastAPI):
}, },
) )
@app.get("/schedule/{channel}", response_class=RedirectResponse)
async def get_channel_default(channel: str): @router.get("/schedule/{channel}", response_class=RedirectResponse)
async def get_channel_default(channel: str):
return RedirectResponse(f"{channel}/tag/today") return RedirectResponse(f"{channel}/tag/today")
@app.get("/schedule/{channel}/tag/{tag}", response_class=HTMLResponse)
async def get_channel_tag(request: AppRequest, channel: str, tag: str): @router.get("/schedule/{channel}/tag/{tag}", response_class=HTMLResponse)
async def get_channel_tag(request: AppRequest, channel: str, tag: str):
tag_value = TagUtil.parse_tag(tag) tag_value = TagUtil.parse_tag(tag)
schedule_api = request.app.state.api.schedule schedule_api = request.app.state.api.schedule
if tag_value.type == TagType.DAY: if tag_value.type == TagType.DAY:
@@ -74,7 +70,6 @@ def mount(app: FastAPI):
request=request, request=request,
name="channel.html", name="channel.html",
context={ context={
"version": __version__,
"tag_util": TagUtil, "tag_util": TagUtil,
"datetime": datetime, "datetime": datetime,
"response": response, "response": response,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,25 +0,0 @@
table.schedule-table {
width: 60rem;
margin: auto;
table-layout: auto
}
table.schedule-table tr {
border-bottom: 1px solid lightgray;
}
table.schedule-table td {
text-align: left;
}
table.schedule-table tr.live {
font-weight: bold;
}
table.schedule-table .title {
margin-top: 0.5rem;
font-style: italic;
font-weight: bold;
font-size: 120%;
background-color: lightgray;
}

View File

@@ -1,28 +1,31 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %} {% block title %}
Программа | {{response.channel.name}} | {{response.date.strftime('%a, %d %B %Y')}} {{_("TV program")}} | {{response.channel.name}} | {{format_date(response.date, DATE_FORMAT,
{% endblock %} locale=request.state.language)}}
{% block head %}
{{ super() }}
<link rel="stylesheet"
href="/static/schedule/style.css?v={{version}}">
<link rel="icon"
href="/static/schedule/favicon.ico?v={{version}}"
type="image/x-icon">
{% endblock %} {% endblock %}
{% block header %} {% block header %}
<a class="button {{'disabled' if response.date == datetime.date.today() else ''}}" <app-link href="/schedule"
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">⬅️</a> icon="tv">{{_("TV program")}}</app-link>
<a class="button"
href="../..">⬆️</a>
<span>{{response.channel.name}} | {{response.date.strftime('%a, %d %B %Y')}}</span>
<a class="button"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">➡️</a>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<table class="schedule-table"> <h4>
<a class="icon-link {{'disabled' if response.date == datetime.date.today() else ''}}"
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">
<i class="bi bi-arrow-left-square"></i>
</a>
<a class="icon-link"
href="../..">
<i class="bi bi-arrow-up-square"></i>
</a>
<span>{{response.channel.name}} | {{format_date(response.date, DATE_FORMAT, locale=request.state.language)}}</span>
<a class="icon-link"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">
<i class="bi bi-arrow-right-square"></i>
</a>
</h4>
<table class="table">
<thead> <thead>
<tr> <tr>
<td></td> <td></td>
@@ -32,7 +35,7 @@
</thead> </thead>
<tbody> <tbody>
{% for value in response.values %} {% for value in response.values %}
<tr class="{{'live' if value.live else ''}}"> <tr class="{{'table-success' if value.live else ''}}">
<td>{{value.start.strftime('%H:%M')}}</td> <td>{{value.start.strftime('%H:%M')}}</td>
<td>{{(value.end - value.start) | timedelta_format}}</td> <td>{{(value.end - value.start) | timedelta_format}}</td>
<td>{{value.label}}</td> <td>{{value.label}}</td>

View File

@@ -1,21 +1,18 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}ТВ{% endblock %} {% block title %}{{_("TV program")}}{% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet"
href="/static/schedule/style.css?v={{version}}">
<link rel="icon"
href="/static/schedule/favicon.ico?v={{version}}"
type="image/x-icon">
{% endblock %}
{% block header %}Телепрограмма{% endblock %}
{% block content %} {% block content %}
<ul class="app-list"> <h1>{{_("TV program")}}</h1>
<li style="margin-bottom: 0.25rem; font-weight: bold;"><a href="schedule/tag/today">Все</a></li> <div class="list-group mb-5">
<a href="schedule/tag/today"
class="list-group-item list-group-item-action px-4">
<span class="fw-bold">Все</span>
</a>
{% for channel in channels %} {% for channel in channels %}
<li><a href="schedule/{{channel.id}}">{{channel.name}}</a></li> <a href="schedule/{{channel.id}}"
class="list-group-item list-group-item-action px-4">
<span class="text-primary">{{channel.name}}</span>
</a>
{% endfor %} {% endfor %}
</ul> </div>
{% endblock %} {% endblock %}

View File

@@ -1,29 +1,33 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %} {% block title %}
{{'Прямые трансляции' if live else 'Программа'}} | {{response.date.strftime('%a, %d %B %Y')}} {{_("Live broadcasts") if live else _("TV program")}} | {{format_date(response.date, DATE_FORMAT,
{% endblock %} locale=request.state.language)}}
{% block head %}
{{ super() }}
<link rel="stylesheet"
href="/static/schedule/style.css?v={{version}}">
<link rel="icon"
href="/static/schedule/favicon.ico?v={{version}}"
type="image/x-icon">
{% endblock %} {% endblock %}
{% block header %} {% block header %}
<a class="button {{'disabled' if response.date == datetime.date.today() else ''}}" <app-link href="/schedule"
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">⬅️</a> icon="tv">{{_("TV program")}}</app-link>
<a class="button"
href="..">⬆️</a>
<span>{{'Прямые трансляции' if live else 'Программа'}} | {{response.date.strftime('%a, %d %B %Y')}}</span>
<a class="button"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">➡️</a>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h4>
<a class="icon-link {{'disabled' if response.date == datetime.date.today() else ''}}"
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">
<i class="bi bi-arrow-left-square"></i>
</a>
<a class="icon-link"
href="..">
<i class="bi bi-arrow-up-square"></i>
</a>
<span>{{_("Live broadcasts") if live else _("TV program")}} | {{format_date(response.date, DATE_FORMAT,
locale=request.state.language)}}</span>
<a class="icon-link"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">
<i class="bi bi-arrow-right-square"></i>
</a>
</h4>
<div> <div>
<table class="schedule-table {{'live' if live else ''}}"> <table class="table">
<thead> <thead>
<tr> <tr>
<td></td> <td></td>
@@ -35,15 +39,13 @@
{% for response in responses %} {% for response in responses %}
{% set values = (response.values|selectattr('live') if live else response.values)|list %} {% set values = (response.values|selectattr('live') if live else response.values)|list %}
{% if values|length > 0 %} {% if values|length > 0 %}
<tr class="title"> <tr class="table-primary fs-4">
<td colspan="3"> <td colspan="3">
<div>{{response.channel.name}}</div> <div>{{response.channel.name}}</div>
</td> </td>
<td></td>
<td></td>
</tr> </tr>
{% for value in values %} {% for value in values %}
<tr class="{{'live' if not live and value.live else ''}}"> <tr class="{{'table-success' if not live and value.live else ''}}">
<td>{{value.start.strftime('%H:%M')}}</td> <td>{{value.start.strftime('%H:%M')}}</td>
<td>{{(value.end - value.start) | timedelta_format}}</td> <td>{{(value.end - value.start) | timedelta_format}}</td>
<td>{{value.label}}</td> <td>{{value.label}}</td>

View File

@@ -0,0 +1,31 @@
import gettext
from contextvars import ContextVar
from fastapi import Cookie, Header, Request
from gallery.util import root_path
_translation: ContextVar[gettext.GNUTranslations | gettext.NullTranslations] = ContextVar("translation")
async def set_language(
request: Request,
accept_language: str = Header("en"),
language: str | None = Cookie(None),
):
# Simplify the header (e.g., "en-US,en;q=0.9" -> "en")
lang = language or accept_language.split(",")[0].split("-")[0]
try:
t = gettext.translation("messages", localedir=root_path / "locales", languages=[lang])
except FileNotFoundError:
t = gettext.NullTranslations()
token = _translation.set(t)
request.state.language = lang
yield lang
_translation.reset(token)
def _(message: str) -> str:
return _translation.get().gettext(message)

View File

@@ -1,87 +1,72 @@
import datetime import datetime
from pathlib import Path from pathlib import Path
from fastapi import FastAPI from fastapi import APIRouter
from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.sketch.weather.catalog import BUNDLE
from gallery.sketch.weather.mock import WEATHER_MOCK_DATA
from gallery.sketch.weather.model import WeatherResponse from gallery.sketch.weather.model import WeatherResponse
from gallery.version import __version__
from ..common.util import TagType, TagUtil from ..common.utils.tag import TagType, TagUtil
from ..common.utils.template import build_templates
from .filters import cloudness_icon, wind_direction_icon from .filters import cloudness_icon, wind_direction_icon
templates = build_templates(
Path(__file__).parent / "templates",
{
"wind_direction_icon": wind_direction_icon,
"cloudness_icon": cloudness_icon,
},
)
def mount(app: FastAPI):
base_dir = Path(__file__).parent
app.mount("/static/weather", StaticFiles(directory=base_dir / "static"))
templates = Jinja2Templates(
directory=[
base_dir.parent / "common/templates",
base_dir / "templates",
]
)
templates.env.filters["wind_direction_icon"] = wind_direction_icon
templates.env.filters["cloudness_icon"] = cloudness_icon
def build_weather_response(request: AppRequest, response: WeatherResponse): def build_weather_response(request: AppRequest, response: WeatherResponse):
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="weather.html", name="weather.html",
context={ context={
"version": __version__,
"tag_util": TagUtil,
"datetime": datetime,
"response": response, "response": response,
}, },
) )
@app.get("/weather", response_class=HTMLResponse)
async def get_weather_list(request: AppRequest): router = APIRouter()
@router.get("/weather", response_class=HTMLResponse)
async def get_weather_index(request: AppRequest, query: str | None = None):
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
locations = await weather_api.get_locations() locations = (await weather_api.find_locations(query)) if query else []
locations_data = BUNDLE.select_items(locations)
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="index.html", name="index.html",
context={ context={
"version": __version__, "locations": locations,
"locations": locations_data,
}, },
) )
@app.get("/weather/{location}", response_class=RedirectResponse)
async def get_weather_default(location: str): @router.get("/weather/{location}", response_class=RedirectResponse)
async def get_weather_default(location: str):
return RedirectResponse(f"{location}/tag/today") return RedirectResponse(f"{location}/tag/today")
@app.get("/weather/{location}/day/mock", response_class=HTMLResponse)
async def get_weather_day_mock(request: AppRequest):
response = WEATHER_MOCK_DATA.get_response("day")
return build_weather_response(request, response)
@app.get("/weather/{location}/days/mock", response_class=HTMLResponse) @router.get("/weather/{location}/day/{date}", response_class=HTMLResponse)
async def get_weather_days_mock(request: AppRequest): async def get_weather_day(request: AppRequest, location: str, date: datetime.date):
response = WEATHER_MOCK_DATA.get_response("days")
return build_weather_response(request, response)
@app.get("/weather/{location}/day/{date}", response_class=HTMLResponse)
async def get_weather_day(request: AppRequest, location: str, date: datetime.date):
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
response = await weather_api.get_day(location, date) response = await weather_api.get_day(location, date)
return build_weather_response(request, response) return build_weather_response(request, response)
@app.get("/weather/{location}/days/{days}", response_class=HTMLResponse)
async def get_weather_days(request: AppRequest, location: str, days: int): @router.get("/weather/{location}/days/{days}", response_class=HTMLResponse)
async def get_weather_days(request: AppRequest, location: str, days: int):
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
response = await weather_api.get_days(location, days) response = await weather_api.get_days(location, days)
return build_weather_response(request, response) return build_weather_response(request, response)
@app.get("/weather/{location}/tag/{tag}", response_class=HTMLResponse)
async def get_weather_tag(request: AppRequest, location: str, tag: str): @router.get("/weather/{location}/tag/{tag}", response_class=HTMLResponse)
async def get_weather_tag(request: AppRequest, location: str, tag: str):
tag_value = TagUtil.parse_tag(tag) tag_value = TagUtil.parse_tag(tag)
weather_api = request.app.state.api.weather weather_api = request.app.state.api.weather
if tag_value.type == TagType.DAY: if tag_value.type == TagType.DAY:

View File

@@ -1,3 +1,5 @@
import datetime
from gallery.sketch.weather.model import ( from gallery.sketch.weather.model import (
Cloudness, Cloudness,
Precipitation, Precipitation,
@@ -9,40 +11,45 @@ from gallery.sketch.weather.model import (
def wind_direction_icon(wind_direction_deg: float) -> str: def wind_direction_icon(wind_direction_deg: float) -> str:
wind_direction = WindDirectionDeg(wind_direction_deg).direction wind_direction = WindDirectionDeg(wind_direction_deg).direction
return { if wind_direction == WindDirection.CALM:
WindDirection.N: "⬇️", return "wind-calm"
WindDirection.NE: "↙️", else:
WindDirection.E: "⬅️", return f"wind-from-{wind_direction.name.lower()}"
WindDirection.SE: "↖️",
WindDirection.S: "⬆️",
WindDirection.SW: "↗️",
WindDirection.W: "➡️",
WindDirection.NW: "↘️",
WindDirection.CALM: "",
}.get(wind_direction, wind_direction)
def cloudness_icon(sky: Sky) -> list[str]: def cloudness_icon(sky: Sky, date: datetime.datetime, period: str) -> list[str]:
day = (3 < date.hour < 22) if period == "day" else True
day_prefix = "day" if day else "night-alt"
main_icon = "" main_icon = ""
if sky.thunder: if sky.thunder:
if sky.cloudness == Cloudness.CLEAR: main_icon = {
main_icon = "🌩️" Precipitation.NO: "lightning",
if sky.precipitation == Precipitation.NO: Precipitation.SMALL_RAIN: "storm-showers",
main_icon = "" Precipitation.RAIN: "thunderstorm",
else: Precipitation.HEAVY_RAIN: "thunderstorm",
main_icon = "⛈️" Precipitation.SHOWER: "thunderstorm",
Precipitation.SNOW: "storm-showers",
Precipitation.HEAVY_SNOW: "storm-showers",
}[sky.precipitation]
if sky.cloudness == Cloudness.PARTLY_CLOUDY:
main_icon = f"{day_prefix}-{main_icon}"
elif sky.precipitation == Precipitation.NO: elif sky.precipitation == Precipitation.NO:
main_icon = { main_icon = {
Cloudness.CLEAR: "☀️", Cloudness.CLEAR: "day-sunny" if day else "night-clear",
Cloudness.PARTLY_CLOUDY: "🌤️", Cloudness.PARTLY_CLOUDY: f"{day_prefix}-cloudy",
Cloudness.CLOUDY: "", Cloudness.CLOUDY: "cloud",
Cloudness.MAINLY_CLOUDY: "☁️", Cloudness.MAINLY_CLOUDY: "cloudy",
}[sky.cloudness] }[sky.cloudness]
elif sky.precipitation in [Precipitation.SNOW, Precipitation.HEAVY_SNOW]:
main_icon = "🌨️"
else: else:
main_icon = "🌧️" main_icon = {
Precipitation.SMALL_RAIN: "showers",
Precipitation.RAIN: "rain-mix",
Precipitation.HEAVY_RAIN: "rain",
Precipitation.SHOWER: "rain",
Precipitation.SNOW: "snow",
Precipitation.HEAVY_SNOW: "snow",
}[sky.precipitation]
if sky.cloudness == Cloudness.PARTLY_CLOUDY:
main_icon = f"{day_prefix}-{main_icon}"
icons = [main_icon] icons = [main_icon]
if sky.fog:
icons.append("🌫️")
return icons return icons

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,72 +0,0 @@
.header {
font-size: 1rem;
text-align: left;
padding-top: 0.25rem;
}
.date {
font-size: 1.5rem;
background: rgba(0, 0, 0, 0.1);
}
.date.now {
background: rgba(0, 128, 255, 0.2);
}
.date .value a {
all: unset;
cursor: pointer;
}
.cloudness {
vertical-align: top;
}
.cloudness .icon {
font-size: 1rem;
}
.cloudness .icon:first-child {
font-size: 2rem;
}
.temperature {
padding: 0;
}
.temperature .value {
padding: 0.1rem 0.4rem;
}
.temperature .value.positive {
color: orangered;
}
.temperature .value.negative {
color: blue;
}
.wind .direction {
font-size: 1rem;
}
.wind .gust {
font-size: 1rem;
}
.precipitation .value {
color: blue;
}
.pressure {
padding: 0;
}
.pressure .value {
padding: 0.1rem 0.4rem;
color: blueviolet;
}
.humidity .value {
color: blue;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -1,20 +1,74 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Погода{% endblock %} {% block title %}{{_("Weather")}}{% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet"
href="/static/weather/style.css?v={{version}}">
<link rel="icon"
href="/static/weather/favicon.ico?v={{version}}"
type="image/x-icon">
{% endblock %}
{% block header %}Погода{% endblock %}
{% block content %} {% block content %}
<ul class="app-list"> <h1>{{_("Weather")}}</h1>
<form action=""
method="get"
class="mb-4">
<div class="input-group mb-3">
<input type="text"
class="form-control"
id="query"
name="query"
placeholder="{{_('Enter the city name')}}">
<button class="btn btn-primary"
type="submit">{{_("Search")}}</button>
</div>
</form>
<ul id="locations"
class="list-group mb-5">
{% for location in locations %} {% for location in locations %}
<li><a href="weather/{{location.id}}">{{location.name}}</a></li> <a href="weather/{{location.id}}"
class="list-group-item list-group-item-action px-4"
onclick="saveLocation({id:'{{location.id}}', name:'{{location.name}}'});">
<span class="fi fi-{{location.country_code}} me-1"></span>
<span class="text-primary">{{location.name}}</span>
<span class="small ms-1 text-secondary">
{{location.country}}, {{location.district}}, {{location.subdistrict}}
</span>
<span></span>
</a>
{% endfor %} {% endfor %}
</ul> </ul>
<script>
(function () {
document.loadLocations = () => {
const locations = JSON.parse(window.localStorage.getItem('locations') || '{}');
const container = document.querySelector('#locations');
container.innerHTML = '';
for (const [id, name] of Object.entries(locations)) {
const element = document.createElement('a');
element.href = `weather/${id}`;
element.className = 'list-group-item list-group-item-action px-4 d-flex justify-content-between align-items-start';
element.innerHTML = `
<span class="text-primary me-auto">${name}</span>
<span class="text-danger" onclick="removeLocation('${id}'); event.preventDefault();">&#x2715;</span>
`;
container.appendChild(element);
}
}
document.saveLocation = (location) => {
const locations = JSON.parse(window.localStorage.getItem('locations') || '{}');
locations[location.id] = location.name;
window.localStorage.setItem('locations', JSON.stringify(locations));
}
document.removeLocation = (id) => {
const locations = JSON.parse(window.localStorage.getItem('locations') || '{}');
delete locations[id];
window.localStorage.setItem('locations', JSON.stringify(locations));
document.loadLocations();
}
const params = new URLSearchParams(window.location.search);
const searchQuery = params.get('query');
if (searchQuery) {
document.querySelector('#query').value = searchQuery;
} else {
document.loadLocations();
}
})();
</script>
{% endblock %} {% endblock %}

View File

@@ -1,32 +1,37 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Погода | {{response.location}} | {{response.date.strftime('%a, %d %B %Y')}}{% endblock %} {% block title %}{{_("Weather")}} | {{response.location}} | {{response.date.strftime('%a, %d %B %Y')}}{% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet"
href="/static/weather/style.css?v={{version}}">
<link rel="icon"
href="/static/weather/favicon.ico?v={{version}}"
type="image/x-icon">
{% endblock %}
{% block header %} {% block header %}
{% if response.period == 'day' %} <app-link href="/weather"
<a class="button {{'disabled' if response.date == datetime.date.today() else ''}}" icon="brightness-high">{{_("Weather")}}</app-link>
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">⬅️</a>
<a class="button"
href="../tag/days-10">⬆️</a>
<span>{{response.location}} | {{response.date.strftime('%a, %d %B %Y')}}</span>
<a class="button"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">➡️</a>
{% endif %}
{% if response.period == 'days' %}
<span>{{response.location}} | {{response.date.strftime('%a, %d %B %Y')}}</span>
{% endif %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div> <h4>
<table style="margin: auto;"> {% if response.period == 'day' %}
<a class="icon-link {{'disabled' if response.date == datetime.date.today() else ''}}"
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">
<i class="bi bi-arrow-left-square"></i>
</a>
<a class="icon-link"
href="../tag/days-10">
<i class="bi bi-arrow-up-square"></i>
</a>
<span>{{response.location}} | {{format_date(response.date, DATE_FORMAT, locale=request.state.language)}}</span>
<a class="icon-link"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">
<i class="bi bi-arrow-right-square"></i>
</a>
{% endif %}
{% if response.period == 'days' %}
<span>{{response.location}} | {{format_date(response.date, DATE_FORMAT, locale=request.state.language)}}</span>
<span>- {{format_date(response.date + datetime.timedelta(days=(response.values | length - 1)), DATE_FORMAT,
locale=request.state.language)}}</span>
{% endif %}
</h4>
<div class="table-responsive">
<table class="table table-weather table-borderless table-compact text-center w-auto"
style="font-size: 130%;">
<tbody> <tbody>
<!-- date --> <!-- date -->
<tr> <tr>
@@ -39,9 +44,9 @@
{% endif %} {% endif %}
{% if response.period == 'days' %} {% if response.period == 'days' %}
<td class="date {{'now' if value.date.date() == datetime.date.today() else ''}}"> <td class="date {{'now' if value.date.date() == datetime.date.today() else ''}}">
<span class="value"> <span class="value {{'text-danger' if value.date.weekday() in [5,6] else ''}}">
<a href="../tag/{{tag_util.create_tag('day', value.date.date())}}"> <a href="../tag/{{tag_util.create_tag('day', value.date.date())}}">
{{value.date.strftime('%a %d')}} {{format_date(value.date, 'E d', locale=request.state.language)}}
</a> </a>
</span> </span>
</td> </td>
@@ -52,14 +57,16 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Облачность {{_("Cloudiness")}}
</td> </td>
</tr> </tr>
<tr> <tr>
{% for value in response.values %} {% for value in response.values %}
<td class="cloudness"> <td class="cloudness"
{% for icon in value.sky | cloudness_icon %} data-bs-toggle="tooltip"
<div class="icon">{{icon}}</div> data-bs-title="{{ value.sky }}">
{% for icon in value.sky | cloudness_icon(value.date, response.period) %}
<div class="wi wi-{{icon}} wi-xl text-primary"></div>
{% endfor %} {% endfor %}
</td> </td>
{% endfor %} {% endfor %}
@@ -68,7 +75,7 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Температура, °C {{_("Temperature, °C")}}
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -87,13 +94,15 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Направление ветра {{_("Wind direction")}}
</td> </td>
</tr> </tr>
<tr> <tr>
{% for value in response.values %} {% for value in response.values %}
<td class="wind"> <td class="wind"
<span class="icon">{{value.wind_direction | wind_direction_icon}}</span> data-bs-toggle="tooltip"
data-bs-title="{{ value.wind_direction }}">
<div class="wi wi-wind-deg wi-{{value.wind_direction | wind_direction_icon}} wi-l text-primary"></div>
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>
@@ -101,7 +110,7 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Скорость ветра, м/с {{_("Wind speed, m/s")}}
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -121,7 +130,7 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Осадки, мм {{_("Precipitation, mm")}}
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -136,7 +145,7 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Давление, мм рт. ст. {{_("Pressure, mmHg")}}
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -154,7 +163,7 @@
<tr> <tr>
<td colspan="{{response.values | length}}" <td colspan="{{response.values | length}}"
class="header"> class="header">
Влажность, % {{_("Humidity, %")}}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -1,13 +1,14 @@
import datetime import datetime
import json
import logging import logging
from typing import Any, Dict, List from typing import Any
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from fastapi import HTTPException, status
from gallery.sketch.source import ApiSource from gallery.sketch.source import ApiSource
from gallery.sketch.weather.api import WeatherApi from gallery.sketch.weather.api import WeatherApi
from gallery.sketch.weather.catalog import LocationId from gallery.sketch.weather.model import Location, WeatherResponse, WeatherValue
from gallery.sketch.weather.model import WeatherResponse, WeatherValue
from . import datehelp from . import datehelp
from .parser import DAYS_PARSER, LOCATION_PARSER, ONE_DAY_PARSER, ROW_PARSERS from .parser import DAYS_PARSER, LOCATION_PARSER, ONE_DAY_PARSER, ROW_PARSERS
@@ -34,7 +35,7 @@ class GismeteoApi(WeatherApi):
) )
def _parse_oneday(self, date: datetime.date, data: str) -> WeatherResponse: def _parse_oneday(self, date: datetime.date, data: str) -> WeatherResponse:
result: List[Dict[str, Any]] = [] result: list[dict[str, Any]] = []
soup = BeautifulSoup(data, features="html.parser") soup = BeautifulSoup(data, features="html.parser")
location = LOCATION_PARSER.parse_location(data) location = LOCATION_PARSER.parse_location(data)
widget = ONE_DAY_PARSER.parse_widget(soup) widget = ONE_DAY_PARSER.parse_widget(soup)
@@ -52,7 +53,7 @@ class GismeteoApi(WeatherApi):
) )
def _parse_manydays(self, data: str) -> WeatherResponse: def _parse_manydays(self, data: str) -> WeatherResponse:
result: List[Dict[str, Any]] = [] result: list[dict[str, Any]] = []
soup = BeautifulSoup(data, features="html.parser") soup = BeautifulSoup(data, features="html.parser")
location = LOCATION_PARSER.parse_location(data) location = LOCATION_PARSER.parse_location(data)
widget = DAYS_PARSER.parse_widget(soup) widget = DAYS_PARSER.parse_widget(soup)
@@ -69,13 +70,41 @@ class GismeteoApi(WeatherApi):
values=values, values=values,
) )
async def get_locations(self) -> list[str]: async def find_locations(self, query: str) -> list[Location]:
return [ geo = "ru"
LocationId.OREL, latitude = 52.968498
LocationId.ZMIYEVKA, longitude = 36.0695
] data = json.loads(
await self.SOURCE.request(
f"mq/city/q/?q={query}&geo={geo}&latitude={latitude}&longitude={longitude}&limit=10"
)
)
result = []
for item in data["data"]:
result.append(
Location(
id=f"{item['slug']}-{item['id']}",
name=item["translations"]["kk"]["city"]["name"],
lat=item["coordinates"]["latitude"],
lon=item["coordinates"]["longitude"],
country=item["translations"]["kk"]["country"]["name"],
country_code=item["country"]["code"].lower(),
district=item["translations"]["kk"]["district"]["name"],
subdistrict=(
item["translations"]["kk"]["subdistrict"]["name"]
if "subdistrict" in item["translations"]["kk"]
else ""
),
)
)
return result
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse: async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
max_date = datetime.date.today() + datetime.timedelta(days=9)
if date > max_date:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail={"max_date": max_date.strftime("%Y-%m-%d")}
)
data = await self.SOURCE.request(f"weather-{location_id}/{datehelp.dump(date)}") data = await self.SOURCE.request(f"weather-{location_id}/{datehelp.dump(date)}")
return self._parse_oneday(date, data) return self._parse_oneday(date, data)

View File

@@ -4,6 +4,7 @@ from bs4 import Tag
T = TypeVar("T") T = TypeVar("T")
class WidgetParser: class WidgetParser:
def parse_widget(self, tag: Tag) -> Tag: def parse_widget(self, tag: Tag) -> Tag:
raise NotImplementedError raise NotImplementedError

View File

@@ -1,5 +0,0 @@
from pathlib import Path
from gallery.sketch.mock import MockData
GISMETEO_MOCK_DATA = MockData(Path(__file__).parent / "data")

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
import datetime import datetime
import logging
import re import re
from typing import Iterable from typing import Iterable
@@ -15,6 +16,8 @@ from gallery.sketch.weather.model import (
from .core import BaseWidgetParser, RowParser from .core import BaseWidgetParser, RowParser
logger = logging.getLogger("gismeteo")
ONE_DAY_PARSER = BaseWidgetParser(".widget.widget-oneday .widget-items") ONE_DAY_PARSER = BaseWidgetParser(".widget.widget-oneday .widget-items")
DAYS_PARSER = BaseWidgetParser(".widget.widget-days .widget-items") DAYS_PARSER = BaseWidgetParser(".widget.widget-days .widget-items")
@@ -36,16 +39,11 @@ class DateParser(RowParser[datetime.datetime]):
KEY = "date" KEY = "date"
def parse_row(self, tag: Tag) -> Iterable[datetime.datetime]: def parse_row(self, tag: Tag) -> Iterable[datetime.datetime]:
datetime_date_tag = tag.select_one( datetime_time_row = tag.select_one(".widget-row.widget-row-datetime-time")
".widget-row.widget-row-datetime-date > .row-item" if datetime_time_row:
) for item in datetime_time_row.select(".row-item > time-value"):
if datetime_date_tag: timestamp = int(item.attrs["timestamp"])
date_str = datetime_date_tag.find(text=True, recursive=False).text time = datetime.datetime.fromtimestamp(timestamp)
date = dateparser.parse(date_str, languages=["ru"])
for item in tag.select(".widget-row.widget-row-datetime-time > .row-item"):
time_str = item.text
time = dateparser.parse(time_str, languages=["ru"])
time = time.replace(year=date.year, month=date.month, day=date.day)
yield time yield time
else: else:
for item in tag.select(".widget-row.widget-row-date > .row-item"): for item in tag.select(".widget-row.widget-row-date > .row-item"):
@@ -59,6 +57,7 @@ class SkyParser(RowParser[Sky]):
CLOUDNESS_MAP: dict[str, Cloudness] = { CLOUDNESS_MAP: dict[str, Cloudness] = {
"ясно": Cloudness.CLEAR, "ясно": Cloudness.CLEAR,
"безоблачно": Cloudness.CLEAR,
"малооблачно": Cloudness.PARTLY_CLOUDY, "малооблачно": Cloudness.PARTLY_CLOUDY,
"облачно": Cloudness.CLOUDY, "облачно": Cloudness.CLOUDY,
"пасмурно": Cloudness.MAINLY_CLOUDY, "пасмурно": Cloudness.MAINLY_CLOUDY,
@@ -66,8 +65,11 @@ class SkyParser(RowParser[Sky]):
PRECIPITATION_MAP: dict[str, Precipitation] = { PRECIPITATION_MAP: dict[str, Precipitation] = {
"без осадков": Precipitation.NO, "без осадков": Precipitation.NO,
"небольшой дождь": Precipitation.SMALL_RAIN, # TODO: remove it?
"небольшой дождь": Precipitation.SMALL_RAIN, "небольшой дождь": Precipitation.SMALL_RAIN,
"сильный дождь": Precipitation.HEAVY_RAIN, # TODO: remove it?
"сильный дождь": Precipitation.HEAVY_RAIN, "сильный дождь": Precipitation.HEAVY_RAIN,
"ливневый дождь": Precipitation.SHOWER,
"дождь": Precipitation.RAIN, "дождь": Precipitation.RAIN,
"ливень": Precipitation.SHOWER, "ливень": Precipitation.SHOWER,
"снег": Precipitation.SNOW, "снег": Precipitation.SNOW,
@@ -80,22 +82,35 @@ class SkyParser(RowParser[Sky]):
"небольшой мокрый снег": Precipitation.SNOW, "небольшой мокрый снег": Precipitation.SNOW,
} }
THUNDER = "гроза"
FOG = "дымка"
def parse_row(self, tag: Tag) -> Iterable[Sky]: def parse_row(self, tag: Tag) -> Iterable[Sky]:
for item in tag.select(".widget-row[data-row=icon-tooltip] > .row-item"): for item in tag.select(".widget-row[data-row=icon-tooltip] > .row-item"):
sky_str = item.attrs["data-tooltip"] sky_str = item.attrs["data-tooltip"]
values = {item.strip().lower() for item in sky_str.split(",")} values = {item.strip().lower() for item in sky_str.split(",")}
cloudness = Cloudness.CLEAR cloudness = Cloudness.CLEAR
precipitation = Precipitation.NO precipitation = Precipitation.NO
thunder = "гроза" in values thunder = False
fog = "дымка" in values fog = False
if self.THUNDER in values:
thunder = True
values.remove(self.THUNDER)
if self.FOG in values:
fog = True
values.remove(self.FOG)
for k, v in self.CLOUDNESS_MAP.items(): for k, v in self.CLOUDNESS_MAP.items():
if k in values: if k in values:
cloudness = v cloudness = v
values.remove(k)
break break
for k, v in self.PRECIPITATION_MAP.items(): for k, v in self.PRECIPITATION_MAP.items():
if k in values: if k in values:
precipitation = v precipitation = v
values.remove(k)
break break
if values:
logger.warning("unknown sky values: %s:", values)
yield Sky( yield Sky(
cloudness=cloudness, cloudness=cloudness,
precipitation=precipitation, precipitation=precipitation,
@@ -108,21 +123,15 @@ class TemperatureParser(RowParser[list[int]]):
KEY = "temperature" KEY = "temperature"
def parse_row(self, tag: Tag) -> Iterable[list[int]]: def parse_row(self, tag: Tag) -> Iterable[list[int]]:
for item in tag.select( for item in tag.select(".widget-row-chart[data-row=temperature-air] > .chart > .values > .value"):
".widget-row-chart[data-row=temperature-air] > .chart > .values > .value" yield [int(value.attrs["value"]) for value in item.select("temperature-value")]
):
yield [
int(value.attrs["value"]) for value in item.select("temperature-value")
]
class WindSpeedParser(RowParser[int]): class WindSpeedParser(RowParser[int]):
KEY = "wind_speed" KEY = "wind_speed"
def parse_row(self, tag: Tag) -> Iterable[int]: def parse_row(self, tag: Tag) -> Iterable[int]:
for item in tag.select( for item in tag.select(".widget-row-wind > .row-item > .wind-speed > speed-value"):
".widget-row-wind > .row-item > .wind-speed > speed-value"
):
yield int(item.attrs["value"]) yield int(item.attrs["value"])
@@ -152,32 +161,24 @@ class WindDirectionParser(RowParser[WindDirection]):
} }
def parse_row(self, tag: Tag) -> Iterable[float]: def parse_row(self, tag: Tag) -> Iterable[float]:
for item in tag.select( for item in tag.select(".widget-row-wind > .row-item > .wind-speed > .wind-direction"):
".widget-row-wind > .row-item > .wind-speed > .wind-direction"
):
wind_direction_str = item.text.lower().strip() wind_direction_str = item.text.lower().strip()
yield WindDirectionDeg.from_direction( yield WindDirectionDeg.from_direction(self.WIND_DIRECTION_MAP[wind_direction_str]).value
self.WIND_DIRECTION_MAP[wind_direction_str]
).value
class PrecipitationParser(RowParser[float]): class PrecipitationParser(RowParser[float]):
KEY = "precipitation" KEY = "precipitation"
def parse_row(self, tag: Tag) -> Iterable[float]: def parse_row(self, tag: Tag) -> Iterable[float]:
for item in tag.select( for item in tag.select(".widget-row[data-row=precipitation-bars] > .row-item > .item-unit"):
".widget-row[data-row=precipitation-bars] > .row-item > .item-unit" yield float(item.text.replace(",", ".").replace("< ", ""))
):
yield float(item.text.replace(",", "."))
class PressureParser(RowParser[list[int]]): class PressureParser(RowParser[list[int]]):
KEY = "pressure" KEY = "pressure"
def parse_row(self, tag: Tag) -> Iterable[list[int]]: def parse_row(self, tag: Tag) -> Iterable[list[int]]:
for item in tag.select( for item in tag.select(".widget-row-chart[data-row=pressure] > .chart > .values > .value"):
".widget-row-chart[data-row=pressure] > .chart > .values > .value"
):
yield [int(value.attrs["value"]) for value in item.select("pressure-value")] yield [int(value.attrs["value"]) for value in item.select("pressure-value")]

View File

@@ -25,32 +25,20 @@ class MatchTvApi(ScheduleApi):
ChannelId.MATCH_STRANA, ChannelId.MATCH_STRANA,
] ]
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
endpoint = f"tvguide/{channel_id}?date={date:%Y%m%d}" endpoint = f"tvguide/{channel_id}?date={date:%Y%m%d}"
data = await self.SOURCE.request(endpoint) data = await self.SOURCE.request(endpoint)
soup = BeautifulSoup(data, features="html.parser") soup = BeautifulSoup(data, features="html.parser")
values = [] values = []
channel_name = ( channel_name = soup.select_one(".p-tv-guide-header__title").text.replace("Телепрограмма ", "").strip()
soup.select_one(".p-tv-guide-header__title") current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
.text.replace("Телепрограмма ", "")
.strip()
)
current_day = datetime.datetime.combine(
date.today(), datetime.datetime.min.time()
)
end = current_day + datetime.timedelta(days=1, hours=6) end = current_day + datetime.timedelta(days=1, hours=6)
prev_value: ScheduleValue | None = None prev_value: ScheduleValue | None = None
for item in soup.select( for item in soup.select(
".p-tv-guide-schedule-channel-carcass__transmissions .p-tv-guide-schedule-channel-transmission" ".p-tv-guide-schedule-channel-carcass__transmissions .p-tv-guide-schedule-channel-transmission"
): ):
title = item.select_one( title = item.select_one(".p-tv-guide-schedule-channel-transmission__title").text.strip()
".p-tv-guide-schedule-channel-transmission__title" time_str = item.select_one(".p-tv-guide-schedule-channel-transmission__time-block").text.strip()
).text.strip()
time_str = item.select_one(
".p-tv-guide-schedule-channel-transmission__time-block"
).text.strip()
hours, minutes = map(int, time_str.split(":")) hours, minutes = map(int, time_str.split(":"))
item_date = current_day.replace(hour=hours, minute=minutes) item_date = current_day.replace(hour=hours, minute=minutes)
if prev_value is not None and item_date.hour < prev_value.start.hour: if prev_value is not None and item_date.hour < prev_value.start.hour:
@@ -62,6 +50,4 @@ class MatchTvApi(ScheduleApi):
if prev_value is not None: if prev_value is not None:
prev_value.end = item_date prev_value.end = item_date
prev_value = value prev_value = value
return Schedule( return Schedule(channel=Channel(id=channel_id, name=channel_name), date=date, values=values)
channel=Channel(id=channel_id, name=channel_name), date=date, values=values
)

View File

@@ -1,5 +0,0 @@
from pathlib import Path
from gallery.sketch.mock import MockData
MATCHTV_MOCK_DATA = MockData(Path(__file__).parent / "data")

View File

@@ -5,8 +5,7 @@ from collections import defaultdict
from aiocache import cached from aiocache import cached
from gallery.sketch.weather.api import WeatherApi from gallery.sketch.weather.api import WeatherApi
from gallery.sketch.weather.catalog import BUNDLE, LocationId from gallery.sketch.weather.model import Location, WeatherResponse, WeatherValue
from gallery.sketch.weather.model import WeatherResponse, WeatherValue
from gallery.sketch.weather.util import merge_weather_values from gallery.sketch.weather.util import merge_weather_values
from gallery.util import TimeUnit from gallery.util import TimeUnit
@@ -20,11 +19,9 @@ class OpenWeatherApi(WeatherApi):
PROVIDER = "openweather" PROVIDER = "openweather"
SOURCE = OpenWeather("517a6bccceaa1c48127f6199ec3fb7cf") SOURCE = OpenWeather("517a6bccceaa1c48127f6199ec3fb7cf")
async def get_locations(self) -> list[str]: @classmethod
return [ def _parse_location(cls, location_id: str) -> tuple[float, float]:
LocationId.OREL, return tuple(map(float, location_id.split(":", maxsplit=2)))
LocationId.ZMIYEVKA,
]
@cached( @cached(
key_builder=lambda fun, self, location_id: f"api.weather.{self.provider}.source.{location_id}.forecast", key_builder=lambda fun, self, location_id: f"api.weather.{self.provider}.source.{location_id}.forecast",
@@ -32,8 +29,10 @@ class OpenWeatherApi(WeatherApi):
ttl=TimeUnit.DAY, ttl=TimeUnit.DAY,
) )
async def _get_location_forecast(self, location_id: str) -> Forecast: async def _get_location_forecast(self, location_id: str) -> Forecast:
location = BUNDLE.get_item(location_id) return await self.SOURCE.get_forecast(*self._parse_location(location_id))
return await self.SOURCE.get_forecast(location.lat, location.lon)
async def find_locations(self, query: str) -> list[Location]:
raise NotImplementedError
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse: async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:
data: Forecast = await self._get_location_forecast(location_id) data: Forecast = await self._get_location_forecast(location_id)
@@ -42,9 +41,8 @@ class OpenWeatherApi(WeatherApi):
value = FORECAST_ITEM_PARSER.parse(item) value = FORECAST_ITEM_PARSER.parse(item)
if value.date.date() == date: if value.date.date() == date:
values.append(value) values.append(value)
location = BUNDLE.get_item(location_id)
return WeatherResponse( return WeatherResponse(
location=location.name, location=location_id,
date=date, date=date,
period="day", period="day",
values=values, values=values,
@@ -57,13 +55,9 @@ class OpenWeatherApi(WeatherApi):
value = FORECAST_ITEM_PARSER.parse(item) value = FORECAST_ITEM_PARSER.parse(item)
item_date = value.date.replace(hour=0, minute=0) item_date = value.date.replace(hour=0, minute=0)
values_by_date[item_date].append(value) values_by_date[item_date].append(value)
values = [ values = [merge_weather_values(date, values) for date, values in values_by_date.items()]
merge_weather_values(date, values)
for date, values in values_by_date.items()
]
location = BUNDLE.get_item(location_id)
return WeatherResponse( return WeatherResponse(
location=location.name, location=location_id,
date=datetime.date.today(), date=datetime.date.today(),
period="days", period="days",
values=list(sorted(values, key=lambda item: item.date)), values=list(sorted(values, key=lambda item: item.date)),

View File

@@ -1,5 +0,0 @@
from pathlib import Path
from gallery.sketch.mock import MockData
OPENWEATHER_MOCK_DATA = MockData(Path(__file__).parent / "data")

View File

@@ -75,9 +75,7 @@ class OpenWeather:
self._source = ApiSource(self.BASE_URL) self._source = ApiSource(self.BASE_URL)
async def get_forecast(self, lat: float, lon: float) -> Forecast: async def get_forecast(self, lat: float, lon: float) -> Forecast:
endpoint = ( endpoint = f"data/2.5/forecast?lat={lat}&lon={lon}&appid={self._api_key}&units=metric"
f"data/2.5/forecast?lat={lat}&lon={lon}&appid={self._api_key}&units=metric"
)
response = await self._source.request(endpoint) response = await self._source.request(endpoint)
response_data = json.loads(response) response_data = json.loads(response)
return Forecast(**response_data) return Forecast(**response_data)

View File

@@ -24,11 +24,7 @@ class ForecastItemParser:
def parse(self, item: ForecastItem) -> WeatherValue: def parse(self, item: ForecastItem) -> WeatherValue:
item_date = datetime.datetime.fromtimestamp(item.dt, datetime.UTC) item_date = datetime.datetime.fromtimestamp(item.dt, datetime.UTC)
item_date = ( item_date = item_date.replace(tzinfo=datetime.timezone.utc).astimezone(tz=None).replace(tzinfo=None)
item_date.replace(tzinfo=datetime.timezone.utc)
.astimezone(tz=None)
.replace(tzinfo=None)
)
value = build_weather_value(item_date) value = build_weather_value(item_date)
# TODO parse temperature interval flag # TODO parse temperature interval flag
value.temperature = [round(item.main.temp)] value.temperature = [round(item.main.temp)]
@@ -38,12 +34,8 @@ class ForecastItemParser:
value.wind_speed = round(item.wind.speed) value.wind_speed = round(item.wind.speed)
value.wind_gust = round(item.wind.gust) value.wind_gust = round(item.wind.gust)
value.wind_direction = item.wind.deg value.wind_direction = item.wind.deg
value.sky.cloudness = self.CLOUDNESS_MAP.get( value.sky.cloudness = self.CLOUDNESS_MAP.get(item.weather[0].description, Cloudness.CLEAR)
item.weather[0].description, Cloudness.CLEAR value.sky.precipitation = self.PRECIPITATION_MAP.get(item.weather[0].description, Precipitation.NO)
)
value.sky.precipitation = self.PRECIPITATION_MAP.get(
item.weather[0].description, Precipitation.NO
)
if item.rain: if item.rain:
value.precipitation = round(item.rain.interval_3h, 1) value.precipitation = round(item.rain.interval_3h, 1)
return value return value

View File

@@ -60,9 +60,7 @@ class YandexTvApi(ScheduleApi):
async def get_channels(self) -> list[ChannelId]: async def get_channels(self) -> list[ChannelId]:
return list(CHANNELS_MAP.keys()) return list(CHANNELS_MAP.keys())
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
endpoint = f"channel/{CHANNELS_MAP[channel_id]}?date={date:%Y-%m-%d}" endpoint = f"channel/{CHANNELS_MAP[channel_id]}?date={date:%Y-%m-%d}"
data = await self.SOURCE.request(endpoint) data = await self.SOURCE.request(endpoint)
soup = BeautifulSoup(data, features="html.parser") soup = BeautifulSoup(data, features="html.parser")
@@ -70,9 +68,7 @@ class YandexTvApi(ScheduleApi):
raise RuntimeError("Captcha") raise RuntimeError("Captcha")
values = [] values = []
channel_name = soup.select_one(".channel-header__text").text.strip() channel_name = soup.select_one(".channel-header__text").text.strip()
current_day = datetime.datetime.combine( current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
date.today(), datetime.datetime.min.time()
)
end = current_day + datetime.timedelta(days=1, hours=6) end = current_day + datetime.timedelta(days=1, hours=6)
prev_value: ScheduleValue | None = None prev_value: ScheduleValue | None = None
for item in soup.select(".channel-schedule .channel-schedule__event"): for item in soup.select(".channel-schedule .channel-schedule__event"):
@@ -89,6 +85,4 @@ class YandexTvApi(ScheduleApi):
if prev_value is not None: if prev_value is not None:
prev_value.end = item_date prev_value.end = item_date
prev_value = value prev_value = value
return Schedule( return Schedule(channel=Channel(id=channel_id, name=channel_name), date=date, values=values)
channel=Channel(id=channel_id, name=channel_name), date=date, values=values
)

View File

@@ -1,5 +0,0 @@
from pathlib import Path
from gallery.sketch.mock import MockData
YANDEXTV_MOCK_DATA = MockData(Path(__file__).parent / "data")

View File

@@ -1,17 +0,0 @@
import json
class MockData:
def __init__(self, data_dir) -> None:
self._data_dir = data_dir
def get_text(self, key: str) -> str:
return (self._data_dir / f"{key}").read_text()
def get_html(self, key: str) -> str:
return self.get_text(f"{key}.html")
def get_json(self, key: str) -> dict:
data = json.loads(self.get_text(f"{key}.json"))
return data

View File

@@ -11,18 +11,14 @@ class ScheduleApi(Api):
async def get_channels(self) -> list[ChannelId]: async def get_channels(self) -> list[ChannelId]:
raise NotImplementedError raise NotImplementedError
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
raise NotImplementedError raise NotImplementedError
async def get_all_schedules(self, date: datetime.date) -> list[Schedule]: async def get_all_schedules(self, date: datetime.date) -> list[Schedule]:
channels = await self.get_channels() channels = await self.get_channels()
results = [] results = []
for channel in channels: for channel in channels:
results.append( results.append(await self.get_channel_schedule(channel_id=channel, date=date))
await self.get_channel_schedule(channel_id=channel, date=date)
)
if self.INTERVAL > 0: if self.INTERVAL > 0:
await asyncio.sleep(self.INTERVAL) await asyncio.sleep(self.INTERVAL)
return results return results

View File

@@ -27,15 +27,11 @@ class CachedScheduleApi(ScheduleApi, CachedApi[ScheduleApi]):
), ),
**CACHE_PRESET._asdict(), **CACHE_PRESET._asdict(),
) )
async def get_channel_schedule( async def get_channel_schedule(self, channel_id: ChannelId, date: datetime.date) -> Schedule:
self, channel_id: ChannelId, date: datetime.date
) -> Schedule:
return await self._api.get_channel_schedule(channel_id, date) return await self._api.get_channel_schedule(channel_id, date)
@cached( @cached(
key_builder=lambda fun, self, date: ( key_builder=lambda fun, self, date: (f"api.{self.CACHE_KEY}.{self.provider}.all.{date}"),
f"api.{self.CACHE_KEY}.{self.provider}.all.{date}"
),
**CACHE_PRESET._asdict(), **CACHE_PRESET._asdict(),
) )
async def get_all_schedules(self, date: datetime.date) -> list[Schedule]: async def get_all_schedules(self, date: datetime.date) -> list[Schedule]:

View File

@@ -7,9 +7,7 @@ logger = logging.getLogger("source")
class ApiSource: class ApiSource:
DEFAULT_USER_AGENT = ( DEFAULT_USER_AGENT = (
"Mozilla/5.0 (X11; Linux x86_64) " "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
) )
DEFAULT_TIMEOUT = 30.0 DEFAULT_TIMEOUT = 30.0

View File

@@ -1,12 +1,12 @@
import datetime import datetime
from ..api import Api from ..api import Api
from .model import WeatherResponse from .model import Location, WeatherResponse
class WeatherApi(Api): class WeatherApi(Api):
async def get_locations(self) -> list[str]: async def find_locations(self, query: str) -> list[Location]:
raise NotImplementedError raise NotImplementedError
async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse: async def get_day(self, location_id: str, date: datetime.date) -> WeatherResponse:

View File

@@ -5,7 +5,7 @@ from aiocache import cached
from gallery.sketch.cached import DEFAULT_CACHE_PRESET, CachedApi from gallery.sketch.cached import DEFAULT_CACHE_PRESET, CachedApi
from .api import WeatherApi from .api import WeatherApi
from .model import WeatherResponse from .model import Location, WeatherResponse
CACHE_PRESET = DEFAULT_CACHE_PRESET CACHE_PRESET = DEFAULT_CACHE_PRESET
@@ -14,11 +14,11 @@ class CachedWeatherApi(WeatherApi, CachedApi[WeatherApi]):
CACHE_KEY = "weather" CACHE_KEY = "weather"
@cached( @cached(
key_builder=lambda fun, self: f"api.{self.CACHE_KEY}.{self.provider}.locations", key_builder=lambda fun, self, query: f"api.{self.CACHE_KEY}.{self.provider}.locations.{query}",
**CACHE_PRESET._asdict(), **CACHE_PRESET._asdict(),
) )
async def get_locations(self) -> list[str]: async def find_locations(self, query: str) -> list[Location]:
return await self._api.get_locations() return await self._api.find_locations(query)
@cached( @cached(
key_builder=lambda fun, self, location_id, date: ( key_builder=lambda fun, self, location_id, date: (

View File

@@ -1,31 +0,0 @@
from enum import Enum
from gallery.sketch.catalog import CatalogBundle
from .model import Location
class LocationId(str, Enum):
OREL = "orel-4432"
ZMIYEVKA = "zmiyevka-184640"
def __str__(self) -> str:
return self.value
BUNDLE = CatalogBundle(
[
Location(
id=LocationId.OREL,
name="Орёл",
lat=52.9687747,
lon=36.0694937,
),
Location(
id=LocationId.ZMIYEVKA,
name="Змиёвка",
lat=52.672192,
lon=36.380112,
),
]
)

View File

@@ -1,12 +0,0 @@
from pathlib import Path
from gallery.sketch.mock import MockData
from gallery.sketch.weather.model import WeatherResponse
class WeatherMockData(MockData):
def get_response(self, key: str) -> WeatherResponse:
return WeatherResponse(**self.get_json(key))
WEATHER_MOCK_DATA = WeatherMockData(Path(__file__).parent / "data")

View File

@@ -1 +0,0 @@
{"location":"Орел","date":"2024-07-29","period":"day","values":[{"date":"2024-07-29T00:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[20],"wind_speed":1,"wind_gust":1,"wind_direction":"SW","precipitation":0.0,"pressure":[744],"humidity":85},{"date":"2024-07-29T03:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[18],"wind_speed":1,"wind_gust":1,"wind_direction":"W","precipitation":0.6,"pressure":[742],"humidity":96},{"date":"2024-07-29T06:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"rain","thunder":false,"fog":false},"temperature":[19],"wind_speed":1,"wind_gust":2,"wind_direction":"S","precipitation":4.9,"pressure":[741],"humidity":95},{"date":"2024-07-29T09:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"rain","thunder":false,"fog":false},"temperature":[19],"wind_speed":3,"wind_gust":7,"wind_direction":"S","precipitation":3.8,"pressure":[740],"humidity":83},{"date":"2024-07-29T12:00:00","sky":{"cloudness":"clear","precipitation":"no","thunder":false,"fog":false},"temperature":[21],"wind_speed":4,"wind_gust":11,"wind_direction":"W","precipitation":0.0,"pressure":[740],"humidity":54},{"date":"2024-07-29T15:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"no","thunder":false,"fog":false},"temperature":[21],"wind_speed":4,"wind_gust":10,"wind_direction":"SW","precipitation":0.0,"pressure":[738],"humidity":48},{"date":"2024-07-29T18:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"no","thunder":false,"fog":false},"temperature":[19],"wind_speed":3,"wind_gust":10,"wind_direction":"SW","precipitation":0.0,"pressure":[737],"humidity":63},{"date":"2024-07-29T21:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"no","thunder":false,"fog":false},"temperature":[17],"wind_speed":3,"wind_gust":7,"wind_direction":"SW","precipitation":0.0,"pressure":[737],"humidity":77}]}

View File

@@ -1 +0,0 @@
{"location":"Орел","date":"2024-07-29","period":"days","values":[{"date":"2024-07-29T00:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"rain","thunder":false,"fog":false},"temperature":[21,17],"wind_speed":4,"wind_gust":11,"wind_direction":"W","precipitation":9.3,"pressure":[744,737],"humidity":96},{"date":"2024-07-30T00:00:00","sky":{"cloudness":"mainly_cloudy","precipitation":"rain","thunder":true,"fog":false},"temperature":[19,14],"wind_speed":2,"wind_gust":7,"wind_direction":"N","precipitation":11.0,"pressure":[737,733],"humidity":100},{"date":"2024-07-31T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[22,14],"wind_speed":3,"wind_gust":10,"wind_direction":"NW","precipitation":1.8,"pressure":[741,738],"humidity":99},{"date":"2024-07-01T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[24,14],"wind_speed":3,"wind_gust":10,"wind_direction":"W","precipitation":0.1,"pressure":[741,740],"humidity":97},{"date":"2024-07-02T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[24,17],"wind_speed":2,"wind_gust":8,"wind_direction":"W","precipitation":0.2,"pressure":[740],"humidity":84},{"date":"2024-07-03T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"no","thunder":false,"fog":false},"temperature":[25,14],"wind_speed":1,"wind_gust":4,"wind_direction":"N","precipitation":0.0,"pressure":[740,739],"humidity":99},{"date":"2024-07-04T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"no","thunder":false,"fog":false},"temperature":[25,14],"wind_speed":3,"wind_gust":6,"wind_direction":"N","precipitation":0.0,"pressure":[743,740],"humidity":92},{"date":"2024-07-05T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"small_rain","thunder":true,"fog":false},"temperature":[25,15],"wind_speed":3,"wind_gust":7,"wind_direction":"NW","precipitation":2.1,"pressure":[744,743],"humidity":98},{"date":"2024-07-06T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[24,14],"wind_speed":3,"wind_gust":5,"wind_direction":"NW","precipitation":0.3,"pressure":[745,744],"humidity":98},{"date":"2024-07-07T00:00:00","sky":{"cloudness":"party_cloudy","precipitation":"small_rain","thunder":false,"fog":false},"temperature":[26,14],"wind_speed":2,"wind_gust":5,"wind_direction":"NW","precipitation":0.2,"pressure":[747,745],"humidity":95}]}

View File

@@ -14,6 +14,10 @@ class Location(Model):
name: str name: str
lat: float lat: float
lon: float lon: float
country: str
country_code: str
district: str
subdistrict: str
class Cloudness(str, Enum): class Cloudness(str, Enum):
@@ -63,7 +67,9 @@ class WindDirectionDeg(float):
# pylint:disable=too-many-return-statements # pylint:disable=too-many-return-statements
def to_direction(self) -> WindDirection: def to_direction(self) -> WindDirection:
if self > 337.5 or self <= 22.25: if self == -1:
return WindDirection.CALM
elif self > 337.5 or self <= 22.25:
return WindDirection.N return WindDirection.N
elif self <= 67.5: elif self <= 67.5:
return WindDirection.NE return WindDirection.NE
@@ -80,7 +86,7 @@ class WindDirectionDeg(float):
elif self <= 337.5: elif self <= 337.5:
return WindDirection.NW return WindDirection.NW
else: else:
return WindDirection.CALM raise ValueError(self)
@classmethod @classmethod
def from_direction(cls, direction: WindDirection) -> "WindDirectionDeg": def from_direction(cls, direction: WindDirection) -> "WindDirectionDeg":

View File

@@ -23,9 +23,7 @@ def build_weather_value(date: datetime.datetime) -> WeatherValue:
) )
def merge_weather_values( def merge_weather_values(date: datetime.datetime, values: list[WeatherValue]) -> WeatherValue:
date: datetime.datetime, values: list[WeatherValue]
) -> WeatherValue:
result = build_weather_value(date) result = build_weather_value(date)
temperatures = [] temperatures = []
pressures = [] pressures = []

View File

@@ -1,5 +1,11 @@
from pathlib import Path
class TimeUnit: class TimeUnit:
SECOND = 1 SECOND = 1
MINUTE = 60 * SECOND MINUTE = 60 * SECOND
HOUR = 60 * MINUTE HOUR = 60 * MINUTE
DAY = 24 * HOUR DAY = 24 * HOUR
root_path = Path(__file__).parent.parent

View File

@@ -1 +1,4 @@
__version__ = "0.1.0" import tomllib
from pathlib import Path
__version__ = tomllib.loads((Path(__file__).parent.parent / "pyproject.toml").read_text())["tool"]["poetry"]["version"]

View File

@@ -0,0 +1,77 @@
msgid ""
msgstr ""
"Project-Id-Version: Gallery\n"
"Last-Translator: shmyga <shmyga.z@gmail.com>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
msgid "Index"
msgstr "Содержание"
msgid "View"
msgstr "Просмотр"
msgid "Docs"
msgstr "Документация"
msgid "Toggle theme"
msgstr "Переключить тему"
msgid "Light"
msgstr "Светлая"
msgid "Dark"
msgstr "Тёмная"
msgid "Auto"
msgstr "Авто"
msgid "Select language"
msgstr "Выберите язык"
msgid "English"
msgstr "Английский"
msgid "Russian"
msgstr "Русский"
# weather
msgid "Weather"
msgstr "Погода"
msgid "Enter the city name"
msgstr "Введите название города"
msgid "Search"
msgstr "Поиск"
msgid "Cloudiness"
msgstr "Облачность"
msgid "Temperature, °C"
msgstr "Температура, °C"
msgid "Wind direction"
msgstr "Направление ветра"
msgid "Wind speed, m/s"
msgstr "Скорость ветра, м/с"
msgid "Precipitation, mm"
msgstr "Осадки, мм"
msgid "Pressure, mmHg"
msgstr "Давление, мм рт. ст."
msgid "Humidity, %"
msgstr "Влажность, %"
# tv
msgid "TV program"
msgstr "Телепрограмма"
msgid "Live broadcasts"
msgstr "Прямые трансляции"

592
poetry.lock generated
View File

@@ -21,115 +21,174 @@ msgpack = ["msgpack (>=0.5.5)"]
redis = ["redis (>=4.2.0)"] redis = ["redis (>=4.2.0)"]
[[package]] [[package]]
name = "aiohttp" name = "aiohappyeyeballs"
version = "3.9.5" version = "2.6.2"
description = "Async http client/server framework (asyncio)" description = "Happy Eyeballs for asyncio"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.10"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, {file = "aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, {file = "aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, ]
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, [[package]]
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, name = "aiohttp"
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, version = "3.14.1"
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, description = "Async http client/server framework (asyncio)"
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, optional = false
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, python-versions = ">=3.10"
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, groups = ["main"]
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, files = [
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, {file = "aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e"},
{file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, {file = "aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491"},
{file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, {file = "aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, {file = "aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, {file = "aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, {file = "aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, {file = "aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6"},
{file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, {file = "aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df"},
{file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, {file = "aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, {file = "aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, {file = "aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, {file = "aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, {file = "aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, {file = "aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf"},
{file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85"},
{file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, {file = "aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, {file = "aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, {file = "aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, {file = "aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, {file = "aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, {file = "aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, {file = "aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, {file = "aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, {file = "aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199"},
{file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817"},
{file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, {file = "aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, {file = "aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, {file = "aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, {file = "aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, {file = "aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, {file = "aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, {file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, {file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, {file = "aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, {file = "aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, {file = "aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da"},
{file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, {file = "aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6"},
{file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96"},
{file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, {file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451"},
{file = "aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345"},
{file = "aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588"},
{file = "aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780"},
{file = "aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a"},
{file = "aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8"},
{file = "aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15"},
{file = "aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8"},
{file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3"},
{file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba"},
{file = "aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397"},
{file = "aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448"},
{file = "aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004"},
{file = "aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602"},
{file = "aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95"},
{file = "aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444"},
{file = "aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0"},
{file = "aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719"},
{file = "aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec"},
{file = "aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2"},
{file = "aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340"},
{file = "aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3"},
{file = "aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d"},
{file = "aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c"},
{file = "aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365"},
{file = "aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9"},
{file = "aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6"},
{file = "aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035"},
] ]
[package.dependencies] [package.dependencies]
aiosignal = ">=1.1.2" aiohappyeyeballs = ">=2.5.0"
aiosignal = ">=1.4.0"
attrs = ">=17.3.0" attrs = ">=17.3.0"
frozenlist = ">=1.1.1" frozenlist = ">=1.1.1"
multidict = ">=4.5,<7.0" multidict = ">=4.5,<7.0"
yarl = ">=1.0,<2.0" propcache = ">=0.2.0"
typing_extensions = {version = ">=4.4", markers = "python_version < \"3.13\""}
yarl = ">=1.17.0,<2.0"
[package.extras] [package.extras]
speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\" and sys_platform != \"android\" and sys_platform != \"ios\"", "aiodns (>=3.3.0) ; sys_platform != \"android\" and sys_platform != \"ios\"", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\" and sys_platform != \"android\" and sys_platform != \"ios\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""]
[[package]] [[package]]
name = "aiosignal" name = "aiosignal"
version = "1.3.1" version = "1.4.0"
description = "aiosignal: a list of registered asynchronous callbacks" description = "aiosignal: a list of registered asynchronous callbacks"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.9"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"},
{file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"},
] ]
[package.dependencies] [package.dependencies]
frozenlist = ">=1.1.0" frozenlist = ">=1.1.0"
typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""}
[[package]] [[package]]
name = "annotated-types" name = "annotated-types"
@@ -178,23 +237,30 @@ files = [
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "23.2.0" version = "26.1.0"
description = "Classes Without Boilerplate" description = "Classes Without Boilerplate"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.9"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"},
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"},
]
[[package]]
name = "babel"
version = "2.18.0"
description = "Internationalization utilities"
optional = false
python-versions = ">=3.8"
groups = ["app"]
files = [
{file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"},
{file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"},
] ]
[package.extras] [package.extras]
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
dev = ["attrs[tests]", "pre-commit"]
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
tests = ["attrs[tests-no-zope]", "zope-interface"]
tests-mypy = ["mypy (>=1.6) ; platform_python_implementation == \"CPython\" and python_version >= \"3.8\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.8\""]
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
[[package]] [[package]]
name = "beautifulsoup4" name = "beautifulsoup4"
@@ -963,6 +1029,137 @@ files = [
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"] testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "propcache"
version = "0.5.2"
description = "Accelerated property cache"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b"},
{file = "propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c"},
{file = "propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b"},
{file = "propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d"},
{file = "propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e"},
{file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274"},
{file = "propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe"},
{file = "propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d"},
{file = "propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5"},
{file = "propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78"},
{file = "propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959"},
{file = "propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66"},
{file = "propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b"},
{file = "propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27"},
{file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f"},
{file = "propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0"},
{file = "propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82"},
{file = "propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab"},
{file = "propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba"},
{file = "propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a"},
{file = "propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42"},
{file = "propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476"},
{file = "propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33"},
{file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a"},
{file = "propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031"},
{file = "propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42"},
{file = "propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84"},
{file = "propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a"},
{file = "propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117"},
{file = "propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7"},
{file = "propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d"},
{file = "propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704"},
{file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4"},
{file = "propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d"},
{file = "propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757"},
{file = "propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f"},
{file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d"},
{file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa"},
{file = "propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc"},
{file = "propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55"},
{file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568"},
{file = "propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191"},
{file = "propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7"},
{file = "propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96"},
{file = "propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999"},
{file = "propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e"},
{file = "propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80"},
{file = "propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825"},
{file = "propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5"},
{file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4"},
{file = "propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0"},
{file = "propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c"},
{file = "propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0"},
{file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb"},
{file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078"},
{file = "propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335"},
{file = "propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d"},
{file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2"},
{file = "propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821"},
{file = "propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370"},
{file = "propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6"},
{file = "propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe"},
{file = "propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427"},
]
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.8.2" version = "2.8.2"
@@ -1793,109 +1990,124 @@ files = [
[[package]] [[package]]
name = "yarl" name = "yarl"
version = "1.9.4" version = "1.24.2"
description = "Yet another URL library" description = "Yet another URL library"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.10"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, {file = "yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12"},
{file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, {file = "yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0"},
{file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, {file = "yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, {file = "yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, {file = "yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, {file = "yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, {file = "yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, {file = "yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, {file = "yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a"},
{file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53"},
{file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342"},
{file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, {file = "yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4"},
{file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, {file = "yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39"},
{file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, {file = "yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, {file = "yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, {file = "yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, {file = "yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, {file = "yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, {file = "yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, {file = "yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, {file = "yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, {file = "yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294"},
{file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2"},
{file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c"},
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b"},
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5"},
{file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, {file = "yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, {file = "yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, {file = "yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, {file = "yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, {file = "yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, {file = "yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, {file = "yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, {file = "yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, {file = "yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, {file = "yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, {file = "yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d"},
{file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db"},
{file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712"},
{file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, {file = "yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, {file = "yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, {file = "yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, {file = "yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, {file = "yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, {file = "yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, {file = "yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488"},
{file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, {file = "yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b"},
{file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, {file = "yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592"},
{file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, {file = "yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617"},
{file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, {file = "yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92"},
{file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, {file = "yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, {file = "yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, {file = "yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, {file = "yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, {file = "yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8"},
{file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, {file = "yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14"},
{file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, {file = "yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535"},
{file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, {file = "yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14"},
{file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, {file = "yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3"},
{file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, {file = "yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, {file = "yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, {file = "yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, {file = "yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, {file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b"},
{file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, {file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761"},
{file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, {file = "yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8"},
{file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed"},
{file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf"},
{file = "yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe"},
{file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd"},
{file = "yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215"},
{file = "yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d"},
{file = "yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9"},
{file = "yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8"},
] ]
[package.dependencies] [package.dependencies]
idna = ">=2.0" idna = ">=2.0"
multidict = ">=4.0" multidict = ">=4.0"
propcache = ">=0.2.1"
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "7295e9ec7f7492017c5bbda489026f19bbf155f0ea82402d348b0aa4c03beaca" content-hash = "39107ded0683832d1ca3d637dc50fac7dd441375ead30cdb56d64edee13c97e2"

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "gallery" name = "gallery"
version = "0.1.0" version = "0.3.3"
description = "" description = ""
authors = ["shmyga <shmyga.z@gmail.com>"] authors = ["shmyga <shmyga.z@gmail.com>"]
readme = "README.md" readme = "README.md"
@@ -12,11 +12,12 @@ aiohttp = "^3.9.5"
beautifulsoup4 = "^4.12.3" beautifulsoup4 = "^4.12.3"
dateparser = "^1.2.0" dateparser = "^1.2.0"
pydantic = "^2.8.2" pydantic = "^2.8.2"
aiocache = {extras = ["redis"], version = "^0.12.2"} aiocache = { extras = ["redis"], version = "^0.12.2" }
[tool.poetry.group.app.dependencies] [tool.poetry.group.app.dependencies]
fastapi = "^0.111.1" fastapi = "^0.111.1"
jinja2 = "^3.1.4" jinja2 = "^3.1.4"
babel = "^2.18.0"
[tool.poetry.group.test.dependencies] [tool.poetry.group.test.dependencies]
pytest = "^8.3.1" pytest = "^8.3.1"
@@ -34,9 +35,13 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts] [tool.poetry.scripts]
gallery = "gallery.main:run" gallery = "gallery.main:run"
[tool.black]
line-length = 120
[tool.isort]
profile = "black"
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "-p no:warnings" addopts = "-p no:warnings"
asyncio_mode = "auto" asyncio_mode = "auto"
testpaths = ["tests"] testpaths = ["tests"]
[tool.poetry_bumpversion.file."gallery/version.py"]

8
scripts/format Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
TARGET="gallery"
poetry run isort $TARGET
poetry run black $TARGET -q

View File

@@ -2,4 +2,8 @@
set -e set -e
cd "$(dirname $(dirname "$0"))" || exit cd "$(dirname $(dirname "$0"))" || exit
poetry run pylint gallery TARGET="gallery"
poetry run pylint $TARGET
poetry run isort $TARGET --check-only
poetry run black $TARGET -q --check --diff

6
scripts/locales Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
cd locales/ru/LC_MESSAGES || exit
msgfmt messages.po

15
scripts/setup Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
PYTHON_VERSION=3.12
poetry env use ${PYTHON_VERSION}
poetry install
cd static || exit
if [[ -f $HOME/.nvm/nvm.sh ]]; then
source "$HOME/.nvm/nvm.sh"
nvm use
fi
npm ci

19
scripts/version Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -e
cd "$(dirname $(dirname "$0"))" || exit
if [ -z "$1" ]; then
echo "Usage: $0 [version]"
exit 1
fi
if [[ ! -z "$(git status -s)" ]]; then
echo "Uncomitted changes"
exit 1
fi
poetry version $1
(cd static && npm version $1 --allow-same-version)
git add .
git commit -m "ci(version): $1"
git tag $1

2
static/.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
dist/

1
static/.nvmrc Normal file
View File

@@ -0,0 +1 @@
24

1241
static/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
static/package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "gallery",
"version": "0.3.3",
"scripts": {
"build": "vite build",
"dev": "vite build --watch"
},
"author": "shmyga <shmyga.z@gmail.com>",
"license": "ISC",
"description": "",
"dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1",
"flag-icons": "^7.5.0",
"sass": "^1.99.0"
},
"devDependencies": {
"vite": "^8.0.9"
}
}

17
static/src/components.ts Normal file
View File

@@ -0,0 +1,17 @@
class AppLinkElement extends HTMLElement {
static observedAttributes = ["icon", "href"];
constructor() {
super();
this.innerHTML = `
<a href="${this.getAttribute("href")}"
class="d-flex align-items-center text-body text-decoration-none">
<span class="fs-4">
<span class="bi bi-${this.getAttribute("icon")} me-1"></span>
<span>${this.textContent}</span>
</span>
</a>`;
}
}
customElements.define("app-link", AppLinkElement);

10
static/src/index.ts Normal file
View File

@@ -0,0 +1,10 @@
import * as bootstrap from "bootstrap";
import "./components";
import "./language";
import "./main.scss";
import "./theme";
document.addEventListener("DOMContentLoaded", (event) => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map((tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl));
});

64
static/src/language.ts Normal file
View File

@@ -0,0 +1,64 @@
(() => {
const getStoredLanguage = () => {
const m = document.cookie.match(/language=(\w+)/);
return m ? m[1] : null;
};
const setStoredLanguage = (language: string) => (document.cookie = `language=${language}; max-age=34560000; path=/`);
const getPreferredLanguage = () => {
const storedLanguage = getStoredLanguage();
if (storedLanguage) {
return storedLanguage;
}
const result = window.navigator.language.split("-")[0];
return ["en", "ru"].includes(result) ? result : "en";
};
const setLanguage = (language: string) => {};
setLanguage(getPreferredLanguage());
const showActiveLanguage = (language: string, focus = false) => {
const languageSwitcher = document.querySelector("#bd-language");
if (!languageSwitcher) {
return;
}
const languageSwitcherText = document.querySelector("#bd-language-text");
const activeLanguageIcon = document.querySelector(".language-icon-active");
const btnToActive = document.querySelector(`[data-bs-language-value="${language}"]`);
const activeLanguageIconClass = btnToActive.querySelector(".fi").className.match(/fi-[\w-]+/)[0];
document.querySelectorAll("[data-bs-language-value]").forEach((element) => {
element.classList.remove("active");
element.setAttribute("aria-pressed", "false");
});
btnToActive.classList.add("active");
btnToActive.setAttribute("aria-pressed", "true");
const classesToRemove = Array.from(activeLanguageIcon.classList).filter((className) => className.startsWith("fi-"));
activeLanguageIcon.classList.remove(...classesToRemove);
activeLanguageIcon.classList.add(activeLanguageIconClass);
const languageSwitcherLabel = `${languageSwitcherText.textContent} (${btnToActive.dataset.bsLanguageValue})`;
languageSwitcher.setAttribute("aria-label", languageSwitcherLabel);
if (focus) {
languageSwitcher.focus();
}
};
window.addEventListener("DOMContentLoaded", () => {
showActiveLanguage(getPreferredLanguage());
document.querySelectorAll("[data-bs-language-value]").forEach((toggle) => {
toggle.addEventListener("click", () => {
const language = toggle.getAttribute("data-bs-language-value") || "";
setStoredLanguage(language);
setLanguage(language);
showActiveLanguage(language, true);
window.location.reload();
});
});
});
})();

45
static/src/lib/bootstrap-icons.scss vendored Normal file
View File

@@ -0,0 +1,45 @@
.icon-link {
vertical-align: -0.25rem;
}
.bi {
display: inline-block;
text-transform: none;
line-height: 1;
vertical-align: -0.125em;
width: 1em;
height: 1em;
mask-size: contain;
mask-position: 50%;
mask-repeat: no-repeat;
background-color: currentColor;
&.bi-circle-half {
mask-image: url(bootstrap-icons/icons/circle-half.svg);
}
&.bi-moon-stars-fill {
mask-image: url(bootstrap-icons/icons/moon-stars-fill.svg);
}
&.bi-brightness-high {
mask-image: url(bootstrap-icons/icons/brightness-high.svg);
}
&.bi-gear {
mask-image: url(bootstrap-icons/icons/gear.svg);
}
&.bi-sun-fill {
mask-image: url(bootstrap-icons/icons/sun-fill.svg);
}
&.bi-tv {
mask-image: url(bootstrap-icons/icons/tv.svg);
}
&.bi-arrow-left-square {
mask-image: url(bootstrap-icons/icons/arrow-left-square.svg);
}
&.bi-arrow-right-square {
mask-image: url(bootstrap-icons/icons/arrow-right-square.svg);
}
&.bi-arrow-up-square {
mask-image: url(bootstrap-icons/icons/arrow-up-square.svg);
}
}

52
static/src/lib/bootstrap.scss vendored Normal file
View File

@@ -0,0 +1,52 @@
@import "bootstrap/scss/mixins/banner";
@include bsBanner("");
// scss-docs-start import-stack
// Configuration
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/utilities";
// Layout & components
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
//@import "bootstrap/scss/images";
//@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/tables";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/buttons";
//@import "bootstrap/scss/transitions";
@import "bootstrap/scss/dropdown";
//@import "bootstrap/scss/button-group";
@import "bootstrap/scss/nav";
@import "bootstrap/scss/navbar";
//@import "bootstrap/scss/card";
//@import "bootstrap/scss/accordion";
//@import "bootstrap/scss/breadcrumb";
//@import "bootstrap/scss/pagination";
//@import "bootstrap/scss/badge";
//@import "bootstrap/scss/alert";
//@import "bootstrap/scss/progress";
@import "bootstrap/scss/list-group";
//@import "bootstrap/scss/close";
//@import "bootstrap/scss/toasts";
//@import "bootstrap/scss/modal";
@import "bootstrap/scss/tooltip";
//@import "bootstrap/scss/popover";
//@import "bootstrap/scss/carousel";
//@import "bootstrap/scss/spinners";
//@import "bootstrap/scss/offcanvas";
//@import "bootstrap/scss/placeholders";
// Helpers
@import "bootstrap/scss/helpers";
// Utilities
@import "bootstrap/scss/utilities/api";
// scss-docs-end import-stack

View File

@@ -0,0 +1,17 @@
@use "flag-icons/sass/flag-icons" with (
$flag-icons-path: "flag-icons/flags",
$flag-icons-included-countries: (
"gb",
"ru",
"by",
"ua",
"kz",
)
);
.fir {
@extend .fis;
border-radius: 50%;
height: 1em;
border: 1px solid gray;
}

View File

@@ -0,0 +1,22 @@
from pathlib import Path
from string import Template
LIST_ITEM = Template(
""".$name {
mask-image: url(./svg/$name.svg);
}
"""
)
def generate():
work_dir = Path(__file__).parent
data = ""
for item in (work_dir / "svg").glob("*.svg"):
data += LIST_ITEM.substitute({"name": item.stem})
target = work_dir / "classes-list.scss"
target.write_text(data)
if __name__ == "__main__":
generate()

View File

@@ -0,0 +1,84 @@
.wi-day-snow {
mask-image: url(./svg/wi-day-snow.svg);
}
.wi-rain-mix {
mask-image: url(./svg/wi-rain-mix.svg);
}
.wi-thunderstorm {
mask-image: url(./svg/wi-thunderstorm.svg);
}
.wi-day-rain {
mask-image: url(./svg/wi-day-rain.svg);
}
.wi-cloudy {
mask-image: url(./svg/wi-cloudy.svg);
}
.wi-night-clear {
mask-image: url(./svg/wi-night-clear.svg);
}
.wi-day-cloudy {
mask-image: url(./svg/wi-day-cloudy.svg);
}
.wi-day-storm-showers {
mask-image: url(./svg/wi-day-storm-showers.svg);
}
.wi-lightning {
mask-image: url(./svg/wi-lightning.svg);
}
.wi-day-rain-mix {
mask-image: url(./svg/wi-day-rain-mix.svg);
}
.wi-day-showers {
mask-image: url(./svg/wi-day-showers.svg);
}
.wi-storm-showers {
mask-image: url(./svg/wi-storm-showers.svg);
}
.wi-snow {
mask-image: url(./svg/wi-snow.svg);
}
.wi-cloud {
mask-image: url(./svg/wi-cloud.svg);
}
.wi-night-alt-snow {
mask-image: url(./svg/wi-night-alt-snow.svg);
}
.wi-night-alt-lightning {
mask-image: url(./svg/wi-night-alt-lightning.svg);
}
.wi-day-sunny {
mask-image: url(./svg/wi-day-sunny.svg);
}
.wi-night-alt-cloudy {
mask-image: url(./svg/wi-night-alt-cloudy.svg);
}
.wi-night-alt-storm-showers {
mask-image: url(./svg/wi-night-alt-storm-showers.svg);
}
.wi-day-thunderstorm {
mask-image: url(./svg/wi-day-thunderstorm.svg);
}
.wi-wind-deg {
mask-image: url(./svg/wi-wind-deg.svg);
}
.wi-showers {
mask-image: url(./svg/wi-showers.svg);
}
.wi-night-alt-rain-mix {
mask-image: url(./svg/wi-night-alt-rain-mix.svg);
}
.wi-rain {
mask-image: url(./svg/wi-rain.svg);
}
.wi-night-alt-thunderstorm {
mask-image: url(./svg/wi-night-alt-thunderstorm.svg);
}
.wi-night-alt-showers {
mask-image: url(./svg/wi-night-alt-showers.svg);
}
.wi-day-lightning {
mask-image: url(./svg/wi-day-lightning.svg);
}
.wi-night-alt-rain {
mask-image: url(./svg/wi-night-alt-rain.svg);
}

View File

@@ -0,0 +1,107 @@
@mixin wind-rotate( $val: 0deg ) {
-webkit-transform: rotate($val);
-moz-transform: rotate($val);
-ms-transform: rotate($val);
-o-transform: rotate($val);
transform: rotate($val);
}
.wi-wind-calm {
display: none !important;
}
.wi-wind-towards-n {
@include wind-rotate(0deg);
}
.wi-wind-towards-nne {
@include wind-rotate(23deg);
}
.wi-wind-towards-ne {
@include wind-rotate(45deg);
}
.wi-wind-towards-ene {
@include wind-rotate(68deg);
}
.wi-wind-towards-e {
@include wind-rotate(90deg);
}
.wi-wind-towards-ese {
@include wind-rotate(113deg);
}
.wi-wind-towards-se {
@include wind-rotate(135deg);
}
.wi-wind-towards-sse {
@include wind-rotate(158deg);
}
.wi-wind-towards-s {
@include wind-rotate(180deg);
}
.wi-wind-towards-ssw {
@include wind-rotate(203deg);
}
.wi-wind-towards-sw {
@include wind-rotate(225deg);
}
.wi-wind-towards-wsw {
@include wind-rotate(248deg);
}
.wi-wind-towards-w {
@include wind-rotate(270deg);
}
.wi-wind-towards-wnw {
@include wind-rotate(293deg);
}
.wi-wind-towards-nw {
@include wind-rotate(313deg);
}
.wi-wind-towards-nnw {
@include wind-rotate(336deg);
}
.wi-wind-from-n {
@include wind-rotate(180deg);
}
.wi-wind-from-nne {
@include wind-rotate(180+23deg);
}
.wi-wind-from-ne {
@include wind-rotate(180+45deg);
}
.wi-wind-from-ene {
@include wind-rotate(180+68deg);
}
.wi-wind-from-e {
@include wind-rotate(180+90deg);
}
.wi-wind-from-ese {
@include wind-rotate(180+113deg);
}
.wi-wind-from-se {
@include wind-rotate(180+135deg);
}
.wi-wind-from-sse {
@include wind-rotate(180+158deg);
}
.wi-wind-from-s {
@include wind-rotate(180+180deg);
}
.wi-wind-from-ssw {
@include wind-rotate(180+203deg);
}
.wi-wind-from-sw {
@include wind-rotate(180+225deg);
}
.wi-wind-from-wsw {
@include wind-rotate(180+248deg);
}
.wi-wind-from-w {
@include wind-rotate(180+270deg);
}
.wi-wind-from-wnw {
@include wind-rotate(180+293deg);
}
.wi-wind-from-nw {
@include wind-rotate(180+313deg);
}
.wi-wind-from-nnw {
@include wind-rotate(180+336deg);
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M4.61,16.88c0-1.15,0.36-2.17,1.08-3.07c0.72-0.9,1.63-1.48,2.74-1.73c0.31-1.37,1.02-2.49,2.11-3.37s2.35-1.32,3.76-1.32
c1.38,0,2.61,0.43,3.69,1.28s1.78,1.95,2.1,3.29h0.33c0.9,0,1.73,0.22,2.49,0.65s1.37,1.03,1.81,1.79c0.44,0.76,0.67,1.58,0.67,2.48
c0,0.88-0.21,1.7-0.63,2.45s-1,1.35-1.73,1.8c-0.73,0.45-1.54,0.69-2.41,0.72H9.41c-1.34-0.06-2.47-0.57-3.4-1.53
C5.08,19.37,4.61,18.22,4.61,16.88z M6.32,16.88c0,0.87,0.3,1.62,0.9,2.26s1.33,0.98,2.19,1.03h11.19c0.86-0.04,1.59-0.39,2.19-1.03
c0.61-0.64,0.91-1.4,0.91-2.26c0-0.88-0.33-1.63-0.98-2.27c-0.65-0.64-1.42-0.96-2.32-0.96H18.8c-0.11,0-0.17-0.06-0.17-0.18
l-0.07-0.57c-0.11-1.08-0.58-1.99-1.4-2.72c-0.82-0.73-1.77-1.1-2.86-1.1c-1.09,0-2.05,0.37-2.85,1.1
c-0.81,0.73-1.27,1.64-1.37,2.72l-0.08,0.57c0,0.12-0.07,0.18-0.2,0.18H9.27c-0.84,0.1-1.54,0.46-2.1,1.07S6.32,16.05,6.32,16.88z"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M3.89,17.6c0-0.99,0.31-1.88,0.93-2.65s1.41-1.27,2.38-1.49c0.26-1.17,0.85-2.14,1.78-2.88c0.93-0.75,2-1.12,3.22-1.12
c1.18,0,2.24,0.36,3.16,1.09c0.93,0.73,1.53,1.66,1.8,2.8h0.27c1.18,0,2.18,0.41,3.01,1.24s1.25,1.83,1.25,3
c0,1.18-0.42,2.18-1.25,3.01s-1.83,1.25-3.01,1.25H8.16c-0.58,0-1.13-0.11-1.65-0.34S5.52,21,5.14,20.62
c-0.38-0.38-0.68-0.84-0.91-1.36S3.89,18.17,3.89,17.6z M5.34,17.6c0,0.76,0.28,1.42,0.82,1.96s1.21,0.82,1.99,0.82h9.28
c0.77,0,1.44-0.27,1.99-0.82c0.55-0.55,0.83-1.2,0.83-1.96c0-0.76-0.27-1.42-0.83-1.96c-0.55-0.54-1.21-0.82-1.99-0.82h-1.39
c-0.1,0-0.15-0.05-0.15-0.15l-0.07-0.49c-0.1-0.94-0.5-1.73-1.19-2.35s-1.51-0.93-2.45-0.93c-0.94,0-1.76,0.31-2.46,0.94
c-0.7,0.62-1.09,1.41-1.18,2.34l-0.07,0.42c0,0.1-0.05,0.15-0.16,0.15l-0.45,0.07c-0.72,0.06-1.32,0.36-1.81,0.89
C5.59,16.24,5.34,16.87,5.34,17.6z M14.19,8.88c-0.1,0.09-0.08,0.16,0.07,0.21c0.43,0.19,0.79,0.37,1.08,0.55
c0.11,0.03,0.19,0.02,0.22-0.03c0.61-0.57,1.31-0.86,2.12-0.86c0.81,0,1.5,0.27,2.1,0.81c0.59,0.54,0.92,1.21,0.99,2l0.09,0.64h1.42
c0.65,0,1.21,0.23,1.68,0.7c0.47,0.47,0.7,1.02,0.7,1.66c0,0.6-0.21,1.12-0.62,1.57s-0.92,0.7-1.53,0.77c-0.1,0-0.15,0.05-0.15,0.16
v1.13c0,0.11,0.05,0.16,0.15,0.16c1.01-0.06,1.86-0.46,2.55-1.19s1.04-1.6,1.04-2.6c0-1.06-0.37-1.96-1.12-2.7
c-0.75-0.75-1.65-1.12-2.7-1.12h-0.15c-0.26-1-0.81-1.82-1.65-2.47c-0.83-0.65-1.77-0.97-2.8-0.97C16.28,7.29,15.11,7.82,14.19,8.88
z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.56,16.9c0,0.9,0.22,1.73,0.66,2.49s1.04,1.36,1.8,1.8c0.76,0.44,1.58,0.66,2.47,0.66h10.83c0.89,0,1.72-0.22,2.48-0.66
c0.76-0.44,1.37-1.04,1.81-1.8c0.44-0.76,0.67-1.59,0.67-2.49c0-0.66-0.14-1.33-0.42-2C22.62,13.98,23,12.87,23,11.6
c0-0.71-0.14-1.39-0.41-2.04c-0.27-0.65-0.65-1.2-1.12-1.67C21,7.42,20.45,7.04,19.8,6.77c-0.65-0.28-1.33-0.41-2.04-0.41
c-1.48,0-2.77,0.58-3.88,1.74c-0.77-0.44-1.67-0.66-2.7-0.66c-1.41,0-2.65,0.44-3.73,1.31c-1.08,0.87-1.78,1.99-2.08,3.35
c-1.12,0.26-2.03,0.83-2.74,1.73S1.56,15.75,1.56,16.9z M3.27,16.9c0-0.84,0.28-1.56,0.84-2.17c0.56-0.61,1.26-0.96,2.1-1.06
l0.5-0.03c0.12,0,0.19-0.06,0.19-0.18l0.07-0.54c0.14-1.08,0.61-1.99,1.41-2.71c0.8-0.73,1.74-1.09,2.81-1.09
c1.1,0,2.06,0.37,2.87,1.1c0.82,0.73,1.27,1.63,1.37,2.71l0.07,0.58c0.02,0.11,0.09,0.17,0.21,0.17h1.61c0.88,0,1.64,0.32,2.28,0.96
c0.64,0.64,0.96,1.39,0.96,2.27c0,0.91-0.32,1.68-0.95,2.32c-0.63,0.64-1.4,0.96-2.28,0.96H6.49c-0.88,0-1.63-0.32-2.27-0.97
C3.59,18.57,3.27,17.8,3.27,16.9z M9.97,4.63c0,0.24,0.08,0.45,0.24,0.63l0.66,0.64c0.25,0.19,0.46,0.27,0.64,0.25
c0.21,0,0.39-0.09,0.55-0.26s0.24-0.38,0.24-0.62c0-0.24-0.09-0.44-0.26-0.59l-0.59-0.66c-0.18-0.16-0.38-0.24-0.61-0.24
c-0.24,0-0.45,0.08-0.62,0.25C10.05,4.19,9.97,4.39,9.97,4.63z M15.31,9.06c0.69-0.67,1.51-1,2.45-1c0.99,0,1.83,0.34,2.52,1.03
c0.69,0.69,1.04,1.52,1.04,2.51c0,0.62-0.17,1.24-0.51,1.84C19.84,12.48,18.68,12,17.32,12H17C16.75,10.91,16.19,9.93,15.31,9.06z
M16.94,3.78c0,0.26,0.08,0.46,0.23,0.62s0.35,0.23,0.59,0.23c0.26,0,0.46-0.08,0.62-0.23c0.16-0.16,0.23-0.36,0.23-0.62V1.73
c0-0.24-0.08-0.43-0.24-0.59s-0.36-0.23-0.61-0.23c-0.24,0-0.43,0.08-0.59,0.23s-0.23,0.35-0.23,0.59V3.78z M22.46,6.07
c0,0.26,0.07,0.46,0.22,0.62c0.21,0.16,0.42,0.24,0.62,0.24c0.18,0,0.38-0.08,0.59-0.24l1.43-1.43c0.16-0.18,0.24-0.39,0.24-0.64
c0-0.24-0.08-0.44-0.24-0.6c-0.16-0.16-0.36-0.24-0.59-0.24c-0.24,0-0.43,0.08-0.58,0.24l-1.47,1.43
C22.53,5.64,22.46,5.84,22.46,6.07z M23.25,17.91c0,0.24,0.08,0.45,0.25,0.63l0.65,0.63c0.15,0.16,0.34,0.24,0.58,0.24
s0.44-0.08,0.6-0.25c0.16-0.17,0.24-0.37,0.24-0.62c0-0.22-0.08-0.42-0.24-0.58l-0.65-0.65c-0.16-0.16-0.35-0.24-0.57-0.24
c-0.24,0-0.44,0.08-0.6,0.24C23.34,17.47,23.25,17.67,23.25,17.91z M24.72,11.6c0,0.23,0.09,0.42,0.26,0.58
c0.16,0.16,0.37,0.24,0.61,0.24h2.04c0.23,0,0.42-0.08,0.58-0.23s0.23-0.35,0.23-0.59c0-0.24-0.08-0.44-0.23-0.6
s-0.35-0.25-0.58-0.25h-2.04c-0.24,0-0.44,0.08-0.61,0.25C24.8,11.17,24.72,11.37,24.72,11.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.56,16.9c0,1.33,0.46,2.47,1.39,3.43c0.93,0.96,2.06,1.47,3.4,1.53c0.11,0,0.17-0.06,0.17-0.17v-1.37
c0-0.12-0.06-0.18-0.17-0.18c-0.87-0.07-1.6-0.41-2.19-1.04c-0.59-0.62-0.89-1.36-0.89-2.21c0-0.84,0.28-1.57,0.85-2.19
s1.26-0.97,2.1-1.04l0.52-0.08c0.13,0,0.2-0.06,0.2-0.17l0.06-0.51c0.11-1.08,0.56-1.99,1.37-2.71c0.81-0.73,1.76-1.09,2.86-1.09
c1.09,0,2.04,0.36,2.85,1.09c0.81,0.72,1.27,1.63,1.39,2.72l0.07,0.58c0,0.11,0.06,0.17,0.19,0.17h1.6c0.91,0,1.68,0.32,2.32,0.95
c0.64,0.63,0.96,1.39,0.96,2.28c0,0.85-0.3,1.59-0.89,2.21c-0.59,0.62-1.32,0.97-2.19,1.04c-0.13,0-0.2,0.06-0.2,0.18v1.37
c0,0.11,0.07,0.17,0.2,0.17c1.33-0.04,2.46-0.55,3.38-1.51s1.38-2.11,1.38-3.45c0-0.68-0.16-1.37-0.47-2.07
C22.6,13.89,23,12.8,23,11.56c0-0.71-0.14-1.39-0.42-2.04c-0.28-0.65-0.65-1.2-1.12-1.67s-1.03-0.84-1.67-1.12
c-0.65-0.27-1.32-0.41-2.03-0.41c-1.54,0-2.84,0.58-3.88,1.73c-0.86-0.41-1.74-0.62-2.65-0.62c-1.42,0-2.67,0.43-3.76,1.3
s-1.79,1.99-2.1,3.37c-1.1,0.26-2.01,0.83-2.73,1.73S1.56,15.75,1.56,16.9z M9.06,28.17h0.3l5.32-7.85
c0.04-0.04,0.05-0.09,0.02-0.14s-0.07-0.07-0.14-0.07h-2.18l2.3-4.21c0.07-0.14,0.03-0.22-0.14-0.22h-2.94
c-0.08,0-0.15,0.05-0.22,0.14l-2.16,5.72c-0.02,0.14,0.02,0.21,0.14,0.21h2.17L9.06,28.17z M9.94,4.59c0,0.25,0.08,0.46,0.24,0.62
l0.66,0.65c0.42,0.32,0.82,0.32,1.21,0c0.16-0.18,0.24-0.39,0.24-0.64c0-0.23-0.08-0.43-0.24-0.59l-0.64-0.65
c-0.19-0.17-0.39-0.25-0.62-0.25c-0.23,0-0.43,0.08-0.6,0.25C10.02,4.15,9.94,4.36,9.94,4.59z M15.28,9.02
C15.96,8.34,16.79,8,17.76,8c0.98,0,1.81,0.35,2.49,1.04c0.69,0.69,1.03,1.53,1.03,2.52c0,0.61-0.17,1.21-0.51,1.81
c-0.98-0.94-2.13-1.41-3.46-1.41h-0.31C16.74,10.82,16.16,9.84,15.28,9.02z M16.9,3.73c0,0.25,0.08,0.46,0.24,0.62
c0.16,0.16,0.36,0.24,0.61,0.24c0.24,0,0.43-0.08,0.59-0.24c0.16-0.16,0.23-0.37,0.23-0.62V1.69c0-0.24-0.08-0.43-0.23-0.59
c-0.16-0.16-0.35-0.23-0.59-0.23c-0.24,0-0.44,0.08-0.6,0.23S16.9,1.45,16.9,1.69V3.73z M22.42,6.05c0,0.23,0.09,0.43,0.27,0.6
c0.18,0.17,0.37,0.25,0.55,0.25c0.16,0,0.37-0.08,0.62-0.25l1.44-1.43c0.17-0.18,0.25-0.39,0.25-0.63c0-0.24-0.08-0.45-0.25-0.61
c-0.17-0.16-0.37-0.24-0.6-0.24c-0.22,0-0.41,0.08-0.58,0.25l-1.43,1.43C22.51,5.61,22.42,5.82,22.42,6.05z M23.23,17.87
c0,0.23,0.08,0.43,0.24,0.61l0.65,0.66c0.16,0.16,0.36,0.24,0.58,0.24c0.24,0,0.44-0.08,0.6-0.25c0.17-0.17,0.25-0.38,0.25-0.63
c0-0.23-0.08-0.42-0.25-0.57l-0.62-0.66c-0.19-0.16-0.39-0.23-0.62-0.23c-0.23,0-0.43,0.08-0.59,0.24
C23.31,17.44,23.23,17.64,23.23,17.87z M24.68,11.56c0,0.22,0.09,0.41,0.26,0.57c0.17,0.17,0.37,0.25,0.6,0.25h2.04
c0.24,0,0.44-0.08,0.61-0.24c0.17-0.16,0.25-0.35,0.25-0.59c0-0.24-0.09-0.44-0.26-0.61s-0.37-0.25-0.6-0.25h-2.04
c-0.23,0-0.43,0.08-0.6,0.25C24.77,11.12,24.68,11.32,24.68,11.56z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.48,16.95c0,1.32,0.46,2.46,1.37,3.4c0.91,0.94,2.04,1.45,3.38,1.51c0.12,0,0.18-0.06,0.18-0.17v-1.33
c0-0.12-0.06-0.18-0.18-0.18c-0.86-0.04-1.58-0.38-2.17-1s-0.88-1.37-0.88-2.24c0-0.84,0.28-1.58,0.84-2.19
c0.56-0.62,1.26-0.96,2.1-1.03l0.53-0.08c0.11,0,0.16-0.05,0.16-0.14l0.08-0.55c0.12-1.09,0.59-2,1.38-2.72S10,9.16,11.1,9.16
s2.05,0.36,2.86,1.08c0.82,0.72,1.28,1.62,1.38,2.69l0.07,0.58c0.02,0.11,0.1,0.17,0.22,0.17h1.6c0.89,0,1.65,0.32,2.29,0.96
c0.64,0.64,0.96,1.41,0.96,2.31c0,0.87-0.29,1.61-0.88,2.24s-1.31,0.95-2.17,1c-0.13,0-0.2,0.06-0.2,0.18v1.33
c0,0.11,0.07,0.17,0.2,0.17c1.33-0.04,2.45-0.54,3.37-1.49c0.91-0.95,1.37-2.09,1.37-3.42c0-0.68-0.13-1.34-0.38-2.01
c0.78-0.96,1.16-2.08,1.16-3.35c0-0.71-0.14-1.38-0.41-2.03c-0.27-0.65-0.65-1.2-1.12-1.67s-1.03-0.84-1.67-1.12
s-1.33-0.42-2.04-0.42c-1.54,0-2.83,0.58-3.86,1.74c-0.89-0.44-1.81-0.66-2.74-0.66c-1.41,0-2.66,0.44-3.74,1.31s-1.77,2-2.08,3.38
c-1.12,0.26-2.04,0.83-2.75,1.73C1.83,14.76,1.48,15.79,1.48,16.95z M6.83,23.98c0,0.17,0.05,0.34,0.16,0.51
c0.11,0.17,0.27,0.28,0.47,0.35c0.23,0.07,0.45,0.06,0.64-0.04c0.2-0.09,0.33-0.28,0.4-0.56l0.14-0.61c0.05-0.23,0.02-0.44-0.1-0.63
c-0.12-0.2-0.29-0.33-0.52-0.4c-0.23-0.07-0.44-0.04-0.64,0.08S7.06,22.97,7,23.2l-0.14,0.59C6.84,23.85,6.83,23.91,6.83,23.98z
M7.6,21.08c0,0.22,0.08,0.41,0.24,0.57C8,21.83,8.19,21.91,8.4,21.91c0.24,0,0.44-0.08,0.6-0.24c0.17-0.16,0.25-0.35,0.25-0.59
c0-0.23-0.08-0.43-0.25-0.59c-0.17-0.16-0.37-0.24-0.6-0.24c-0.23,0-0.42,0.08-0.58,0.23S7.6,20.85,7.6,21.08z M8.21,18.81
c-0.01,0.16,0.03,0.31,0.13,0.45c0.1,0.15,0.26,0.25,0.48,0.32c0.21,0.06,0.41,0.04,0.62-0.07C9.65,19.4,9.79,19.23,9.86,19
l0.27-0.9c0.07-0.24,0.05-0.46-0.07-0.65c-0.12-0.19-0.3-0.32-0.54-0.39c-0.22-0.07-0.43-0.05-0.63,0.07
c-0.2,0.11-0.34,0.28-0.41,0.5l-0.24,0.92C8.22,18.71,8.21,18.8,8.21,18.81z M9.36,27.1c0,0.17,0.05,0.33,0.16,0.49
c0.11,0.16,0.27,0.27,0.49,0.33c0.09,0.02,0.17,0.03,0.24,0.03c0.43,0,0.7-0.2,0.8-0.61l0.13-0.59c0.06-0.26,0.03-0.48-0.08-0.68
s-0.29-0.32-0.52-0.37c-0.21-0.07-0.42-0.05-0.63,0.07c-0.21,0.12-0.34,0.29-0.41,0.51L9.4,26.88C9.37,26.99,9.36,27.07,9.36,27.1z
M9.92,4.66c0,0.24,0.08,0.44,0.24,0.6l0.66,0.64c0.14,0.16,0.32,0.24,0.54,0.26c0.22,0.02,0.43-0.07,0.62-0.26
c0.16-0.16,0.24-0.36,0.24-0.59c0-0.24-0.08-0.44-0.24-0.6l-0.63-0.65C11.2,3.9,11,3.82,10.77,3.8c-0.23,0-0.43,0.08-0.6,0.25
C10.01,4.22,9.92,4.42,9.92,4.66z M10.15,24.2c0,0.23,0.08,0.42,0.24,0.58c0.16,0.16,0.36,0.24,0.58,0.24
c0.24,0,0.43-0.08,0.59-0.23c0.16-0.16,0.23-0.35,0.23-0.59c0-0.23-0.08-0.42-0.23-0.58c-0.16-0.16-0.35-0.23-0.59-0.23
c-0.24,0-0.43,0.08-0.59,0.23S10.15,23.97,10.15,24.2z M10.77,21.93c-0.01,0.15,0.03,0.31,0.14,0.47c0.1,0.16,0.25,0.26,0.45,0.3
c0.23,0.06,0.44,0.04,0.64-0.06s0.33-0.29,0.41-0.56l0.26-0.9c0.07-0.22,0.05-0.43-0.07-0.63c-0.12-0.2-0.29-0.33-0.53-0.4
c-0.22-0.07-0.43-0.04-0.64,0.08s-0.34,0.3-0.41,0.53l-0.22,0.9C10.78,21.74,10.77,21.83,10.77,21.93z M13.53,24.08
c0,0.17,0.05,0.33,0.15,0.48c0.1,0.15,0.25,0.26,0.46,0.32c0.03,0,0.08,0.01,0.14,0.02c0.06,0.01,0.11,0.02,0.14,0.02
c0.41,0,0.66-0.22,0.76-0.66l0.14-0.6c0.07-0.21,0.05-0.42-0.07-0.63c-0.12-0.21-0.29-0.34-0.51-0.41
c-0.25-0.06-0.48-0.04-0.68,0.08s-0.34,0.29-0.41,0.53l-0.09,0.59c0,0.01,0,0.05-0.01,0.11C13.54,24,13.53,24.04,13.53,24.08z
M14.27,21.12c0,0.23,0.08,0.42,0.24,0.57c0.15,0.16,0.34,0.24,0.58,0.24s0.43-0.08,0.59-0.23c0.16-0.16,0.23-0.35,0.23-0.58
c0-0.24-0.08-0.43-0.23-0.59c-0.16-0.16-0.35-0.23-0.59-0.23s-0.43,0.08-0.59,0.23S14.27,20.88,14.27,21.12z M14.88,18.81
c0,0.17,0.05,0.33,0.16,0.48c0.11,0.15,0.27,0.26,0.49,0.32c0.02,0,0.06,0.01,0.12,0.02c0.06,0.01,0.11,0.02,0.14,0.02
c0.11,0,0.23-0.03,0.37-0.09c0.21-0.11,0.34-0.28,0.4-0.52l0.24-0.9c0.06-0.23,0.04-0.44-0.07-0.63s-0.28-0.33-0.5-0.4
c-0.23-0.07-0.45-0.05-0.64,0.06c-0.2,0.11-0.33,0.27-0.4,0.51l-0.28,0.91c0,0.02,0,0.05-0.01,0.11
C14.89,18.73,14.88,18.77,14.88,18.81z M15.23,9.09c0.66-0.66,1.48-0.99,2.47-0.99c0.99,0,1.83,0.34,2.52,1.02s1.04,1.5,1.04,2.48
c0,0.66-0.18,1.29-0.53,1.88c-0.98-0.98-2.15-1.47-3.5-1.47h-0.31C16.64,10.91,16.07,9.94,15.23,9.09z M16.88,3.83
c0,0.23,0.08,0.42,0.23,0.58c0.15,0.15,0.35,0.23,0.59,0.23c0.24,0,0.45-0.08,0.62-0.23c0.17-0.15,0.25-0.35,0.25-0.58V1.76
c0-0.23-0.09-0.43-0.26-0.6c-0.17-0.17-0.38-0.25-0.61-0.25c-0.23,0-0.43,0.08-0.58,0.25c-0.16,0.17-0.23,0.37-0.23,0.6V3.83z
M22.4,6.09c0,0.25,0.08,0.45,0.23,0.6c0.36,0.36,0.76,0.36,1.21,0l1.43-1.43c0.17-0.17,0.25-0.38,0.25-0.63
c0-0.24-0.08-0.44-0.25-0.6c-0.17-0.17-0.37-0.25-0.6-0.25c-0.23,0-0.43,0.08-0.61,0.24L22.63,5.5C22.48,5.65,22.4,5.84,22.4,6.09z
M23.18,17.94c0,0.23,0.09,0.43,0.27,0.59l0.61,0.63c0.2,0.16,0.4,0.24,0.61,0.24c0.2,0,0.4-0.08,0.6-0.24
c0.17-0.16,0.25-0.35,0.25-0.59c0-0.23-0.08-0.43-0.25-0.62l-0.65-0.61c-0.15-0.17-0.34-0.25-0.57-0.25s-0.43,0.08-0.6,0.25
C23.27,17.51,23.18,17.71,23.18,17.94z M24.66,11.6c0,0.24,0.09,0.43,0.26,0.59c0.18,0.18,0.39,0.27,0.62,0.27h2.03
c0.23,0,0.43-0.08,0.59-0.25c0.16-0.17,0.24-0.37,0.24-0.61c0-0.24-0.08-0.44-0.24-0.6c-0.16-0.17-0.35-0.25-0.59-0.25h-2.03
c-0.24,0-0.44,0.08-0.62,0.25S24.66,11.37,24.66,11.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.51,16.89c0,1.33,0.46,2.48,1.39,3.44s2.06,1.47,3.41,1.53c0.11,0,0.17-0.06,0.17-0.17v-1.34c0-0.11-0.06-0.17-0.17-0.17
c-0.86-0.04-1.59-0.39-2.19-1.03s-0.9-1.4-0.9-2.26c0-0.82,0.28-1.54,0.85-2.16s1.27-0.97,2.1-1.07l0.53-0.05
c0.13,0,0.2-0.06,0.2-0.17l0.07-0.54c0.11-1.08,0.56-1.99,1.37-2.72s1.76-1.1,2.85-1.1c1.09,0,2.04,0.37,2.86,1.1
s1.28,1.64,1.4,2.72l0.07,0.57c0,0.12,0.06,0.19,0.17,0.19h1.62c0.89,0,1.65,0.32,2.3,0.96c0.65,0.64,0.97,1.39,0.97,2.27
c0,0.87-0.3,1.62-0.9,2.26c-0.6,0.64-1.33,0.98-2.18,1.03c-0.12,0-0.19,0.06-0.19,0.17v1.34c0,0.11,0.06,0.17,0.19,0.17
c1.33-0.04,2.46-0.55,3.39-1.51c0.93-0.97,1.39-2.12,1.39-3.45c0-0.72-0.14-1.39-0.42-2.01c0.78-0.97,1.17-2.07,1.17-3.31
c0-0.95-0.24-1.83-0.71-2.64c-0.47-0.81-1.11-1.45-1.92-1.92s-1.68-0.7-2.62-0.7c-1.56,0-2.85,0.58-3.88,1.74
c-0.82-0.44-1.72-0.66-2.71-0.66c-1.41,0-2.67,0.44-3.76,1.32s-1.79,2-2.1,3.36c-1.11,0.26-2.02,0.84-2.74,1.74
S1.51,15.74,1.51,16.89z M6.91,23.75c0,0.17,0.05,0.33,0.16,0.49c0.11,0.16,0.27,0.27,0.49,0.33c0.11,0.02,0.2,0.04,0.27,0.04
c0.39,0,0.65-0.21,0.77-0.64l1.58-5.88c0.07-0.24,0.04-0.46-0.08-0.67c-0.12-0.21-0.3-0.33-0.53-0.38
c-0.22-0.07-0.43-0.05-0.63,0.07c-0.2,0.11-0.34,0.28-0.41,0.51l-1.58,5.91C6.93,23.66,6.91,23.73,6.91,23.75z M9.52,26.83
c0,0.19,0.05,0.36,0.15,0.52c0.1,0.16,0.27,0.26,0.52,0.3c0.11,0.02,0.2,0.04,0.26,0.04c0.16,0,0.31-0.06,0.45-0.17
c0.14-0.12,0.23-0.28,0.27-0.48l2.4-8.93c0.06-0.23,0.04-0.45-0.07-0.64s-0.28-0.33-0.5-0.4c-0.23-0.07-0.45-0.05-0.65,0.07
c-0.2,0.11-0.34,0.28-0.4,0.51l-2.4,8.93C9.53,26.73,9.52,26.82,9.52,26.83z M9.94,4.6c0,0.24,0.08,0.44,0.25,0.61l0.65,0.66
c0.19,0.15,0.4,0.22,0.62,0.22c0.21,0,0.41-0.08,0.58-0.23c0.17-0.16,0.26-0.35,0.26-0.59c0-0.24-0.08-0.46-0.24-0.64l-0.64-0.65
c-0.18-0.17-0.38-0.25-0.6-0.25c-0.24,0-0.45,0.09-0.62,0.26C10.03,4.16,9.94,4.37,9.94,4.6z M13.67,23.77
c0,0.16,0.05,0.32,0.15,0.47s0.26,0.26,0.46,0.32c0.11,0.02,0.2,0.04,0.25,0.04c0.17,0,0.34-0.05,0.49-0.15
c0.15-0.1,0.25-0.26,0.3-0.49l1.58-5.88c0.06-0.23,0.04-0.45-0.07-0.64c-0.11-0.2-0.28-0.33-0.5-0.4c-0.24-0.07-0.45-0.05-0.65,0.07
c-0.2,0.11-0.33,0.28-0.39,0.51l-1.58,5.91C13.69,23.68,13.67,23.76,13.67,23.77z M15.3,9.03c0.71-0.67,1.53-1,2.48-1
c0.98,0,1.82,0.35,2.5,1.04c0.69,0.69,1.03,1.53,1.03,2.52c0,0.62-0.17,1.24-0.52,1.85c-0.97-0.97-2.13-1.45-3.49-1.45h-0.33
C16.7,10.81,16.14,9.83,15.3,9.03z M16.92,3.78c0,0.23,0.08,0.43,0.25,0.59c0.17,0.16,0.37,0.24,0.61,0.24
c0.23,0,0.43-0.08,0.59-0.23c0.16-0.16,0.24-0.35,0.24-0.59V1.73c0-0.26-0.08-0.47-0.23-0.63c-0.16-0.16-0.35-0.24-0.59-0.24
c-0.25,0-0.46,0.08-0.62,0.25s-0.24,0.37-0.24,0.62V3.78z M22.45,6.06c0,0.24,0.09,0.44,0.27,0.59c0.14,0.16,0.32,0.24,0.55,0.26
c0.23,0.02,0.44-0.07,0.62-0.26l1.44-1.43c0.18-0.17,0.26-0.38,0.26-0.63c0-0.24-0.08-0.45-0.25-0.61
c-0.17-0.16-0.37-0.24-0.61-0.24c-0.21,0-0.4,0.08-0.58,0.25l-1.43,1.44C22.54,5.6,22.45,5.81,22.45,6.06z M23.26,17.91
c0,0.24,0.08,0.45,0.24,0.63l0.65,0.63c0.18,0.14,0.38,0.21,0.6,0.21l0.02,0.02c0.23,0,0.42-0.08,0.58-0.24
c0.16-0.16,0.24-0.37,0.24-0.61c0-0.24-0.09-0.43-0.26-0.58l-0.62-0.66c-0.18-0.16-0.39-0.24-0.62-0.24s-0.43,0.08-0.59,0.25
S23.26,17.67,23.26,17.91z M24.72,11.58c0,0.24,0.09,0.43,0.26,0.59c0.18,0.18,0.38,0.26,0.62,0.26h2.03c0.24,0,0.44-0.08,0.61-0.25
c0.17-0.17,0.25-0.37,0.25-0.6c0-0.24-0.08-0.44-0.25-0.61s-0.37-0.26-0.61-0.26H25.6c-0.24,0-0.44,0.09-0.62,0.26
C24.8,11.14,24.72,11.34,24.72,11.58z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.56,16.88c0,1.33,0.46,2.47,1.39,3.43s2.06,1.46,3.4,1.53c0.11,0,0.17-0.06,0.17-0.17v-1.34c0-0.12-0.06-0.18-0.17-0.18
c-0.85-0.04-1.57-0.38-2.17-1.02s-0.89-1.39-0.89-2.25c0-0.84,0.28-1.56,0.84-2.17s1.26-0.96,2.1-1.06l0.5-0.04
c0.13,0,0.2-0.06,0.2-0.18l0.06-0.53c0.11-1.08,0.56-1.99,1.37-2.71c0.81-0.73,1.76-1.09,2.86-1.09c1.09,0,2.05,0.36,2.86,1.09
c0.81,0.73,1.27,1.63,1.37,2.71l0.07,0.57c0,0.12,0.06,0.18,0.18,0.18h1.67c0.88,0,1.63,0.32,2.27,0.96
c0.64,0.64,0.96,1.39,0.96,2.27c0,0.85-0.3,1.59-0.9,2.22s-1.32,0.98-2.16,1.05c-0.11,0-0.17,0.06-0.17,0.18v1.34
c0,0.11,0.06,0.17,0.17,0.17c0.88-0.02,1.67-0.26,2.4-0.72s1.3-1.05,1.71-1.8c0.42-0.75,0.62-1.56,0.62-2.44
c0-0.71-0.14-1.37-0.41-1.96c0.76-0.94,1.13-2.03,1.13-3.28c0-0.71-0.14-1.39-0.41-2.04c-0.27-0.65-0.65-1.2-1.12-1.67
C21,7.46,20.44,7.09,19.8,6.82c-0.65-0.28-1.33-0.41-2.04-0.41c-1.51,0-2.78,0.55-3.81,1.66c-0.79-0.43-1.7-0.64-2.73-0.64
c-1.41,0-2.66,0.44-3.75,1.31s-1.77,1.99-2.07,3.35c-1.12,0.26-2.05,0.83-2.77,1.72C1.92,14.7,1.56,15.73,1.56,16.88z M6.97,23.58
c0,0.18,0.05,0.36,0.16,0.53c0.11,0.18,0.26,0.29,0.45,0.36c0.19,0.07,0.4,0.05,0.61-0.06c0.22-0.11,0.36-0.29,0.44-0.55l0.25-1.05
c0.07-0.21,0.05-0.41-0.07-0.62c-0.12-0.21-0.29-0.35-0.51-0.42c-0.25-0.06-0.47-0.03-0.67,0.08s-0.32,0.3-0.37,0.53l-0.28,0.99
C6.98,23.42,6.97,23.49,6.97,23.58z M8.28,18.86c0,0.38,0.21,0.64,0.64,0.79c0.22,0.08,0.43,0.06,0.64-0.05
c0.21-0.11,0.34-0.29,0.41-0.53l0.24-1.03c0.07-0.21,0.05-0.41-0.07-0.62c-0.11-0.21-0.28-0.35-0.51-0.42
c-0.24-0.06-0.47-0.04-0.67,0.08s-0.32,0.29-0.37,0.52l-0.3,1.02C8.29,18.7,8.28,18.78,8.28,18.86z M9.5,26.75
c0,0.16,0.06,0.33,0.17,0.5c0.11,0.17,0.28,0.29,0.49,0.36c0.01,0,0.04,0,0.1,0.01c0.06,0.01,0.11,0.01,0.15,0.01
c0.14,0,0.26-0.02,0.37-0.07c0.19-0.08,0.33-0.27,0.41-0.58l0.27-0.99c0.07-0.23,0.05-0.45-0.07-0.65c-0.12-0.2-0.29-0.34-0.51-0.4
c-0.23-0.07-0.45-0.05-0.65,0.07c-0.2,0.12-0.34,0.29-0.4,0.51l-0.28,1.02C9.51,26.63,9.5,26.7,9.5,26.75z M9.96,4.68
c0,0.25,0.08,0.46,0.25,0.62l0.66,0.65c0.34,0.34,0.73,0.34,1.17,0c0.16-0.17,0.24-0.38,0.24-0.61c0-0.23-0.08-0.43-0.24-0.61
l-0.63-0.66c-0.16-0.16-0.36-0.24-0.6-0.24c-0.23,0-0.43,0.08-0.6,0.25C10.04,4.24,9.96,4.44,9.96,4.68z M10.85,21.96
c0,0.17,0.05,0.34,0.16,0.51c0.11,0.17,0.26,0.28,0.47,0.35c0.23,0.07,0.44,0.05,0.64-0.05c0.19-0.1,0.33-0.29,0.4-0.56l0.24-1.01
c0.07-0.23,0.05-0.45-0.06-0.65c-0.11-0.2-0.28-0.34-0.5-0.41c-0.25-0.07-0.48-0.04-0.68,0.08c-0.2,0.12-0.33,0.3-0.37,0.53
l-0.28,1.03C10.85,21.81,10.85,21.87,10.85,21.96z M13.63,23.68c0.02,0.38,0.23,0.65,0.63,0.83l0.25,0.04
c0.16,0,0.32-0.05,0.47-0.16c0.15-0.11,0.26-0.27,0.32-0.5l0.29-1.01c0.06-0.24,0.03-0.46-0.09-0.66c-0.12-0.2-0.3-0.33-0.53-0.37
c-0.21-0.07-0.41-0.05-0.62,0.07s-0.34,0.29-0.41,0.51l-0.27,1.02c-0.01,0.02-0.01,0.05-0.02,0.08s-0.01,0.06-0.02,0.08
S13.63,23.66,13.63,23.68z M15.03,18.92c0,0.16,0.05,0.32,0.15,0.48c0.1,0.16,0.25,0.27,0.45,0.32l0.25,0.03
c0.19,0,0.37-0.06,0.52-0.18s0.24-0.28,0.28-0.47l0.27-0.99c0.07-0.24,0.05-0.45-0.07-0.65c-0.11-0.2-0.28-0.33-0.51-0.39
c-0.23-0.07-0.45-0.05-0.64,0.06c-0.2,0.11-0.33,0.28-0.39,0.5l-0.3,1.06C15.04,18.77,15.03,18.85,15.03,18.92z M15.36,9.06
c0.66-0.64,1.46-0.96,2.4-0.96c0.98,0,1.82,0.35,2.51,1.04c0.69,0.69,1.04,1.53,1.04,2.51c0,0.56-0.16,1.15-0.47,1.76
c-0.96-0.96-2.11-1.43-3.47-1.43h-0.34C16.77,10.84,16.21,9.87,15.36,9.06z M16.9,3.83c0,0.25,0.08,0.45,0.24,0.61
c0.16,0.16,0.36,0.24,0.61,0.24s0.45-0.08,0.61-0.24c0.16-0.16,0.24-0.36,0.24-0.61V1.81c0-0.25-0.08-0.46-0.24-0.62
c-0.16-0.16-0.36-0.24-0.61-0.24s-0.45,0.08-0.61,0.24c-0.16,0.16-0.24,0.37-0.24,0.62V3.83z M22.45,6.12
c0,0.25,0.08,0.45,0.23,0.61c0.21,0.17,0.41,0.25,0.62,0.25c0.19,0,0.38-0.08,0.59-0.25l1.43-1.43c0.16-0.18,0.24-0.39,0.24-0.63
c0-0.24-0.08-0.44-0.24-0.6c-0.16-0.16-0.36-0.24-0.59-0.24s-0.43,0.08-0.61,0.24L22.68,5.5C22.53,5.68,22.45,5.88,22.45,6.12z
M23.24,17.95c0,0.23,0.09,0.44,0.26,0.63l0.62,0.64c0.21,0.21,0.41,0.31,0.62,0.31c0.19,0,0.39-0.1,0.58-0.31
c0.18-0.18,0.27-0.39,0.26-0.61c-0.01-0.23-0.09-0.43-0.26-0.6l-0.65-0.66c-0.16-0.16-0.35-0.24-0.57-0.24
c-0.24,0-0.44,0.08-0.61,0.25C23.33,17.52,23.24,17.72,23.24,17.95z M24.71,11.64c0,0.22,0.08,0.42,0.24,0.58
c0.16,0.16,0.36,0.24,0.58,0.24h2.04c0.26,0,0.47-0.08,0.63-0.23c0.16-0.16,0.24-0.35,0.24-0.59c0-0.25-0.08-0.46-0.25-0.62
c-0.17-0.16-0.37-0.24-0.62-0.24h-2.04c-0.23,0-0.43,0.08-0.59,0.25C24.79,11.2,24.71,11.41,24.71,11.64z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.58,16.93c0,0.86,0.21,1.67,0.64,2.41c0.42,0.74,1,1.34,1.74,1.79c0.73,0.45,1.54,0.69,2.4,0.71
c0.11,0,0.17-0.06,0.17-0.17v-1.33c0-0.12-0.06-0.19-0.17-0.19c-0.85-0.04-1.58-0.38-2.18-1.02s-0.9-1.37-0.9-2.21
c0-0.82,0.28-1.54,0.85-2.16c0.57-0.61,1.26-0.97,2.1-1.07l0.53-0.06c0.12,0,0.18-0.06,0.18-0.19l0.08-0.51
c0.11-1.09,0.56-2,1.36-2.73c0.8-0.73,1.75-1.09,2.85-1.09c1.09,0,2.04,0.36,2.85,1.09c0.82,0.73,1.28,1.63,1.38,2.7l0.07,0.58
c0,0.11,0.06,0.17,0.17,0.17h1.61c0.9,0,1.67,0.32,2.31,0.96c0.64,0.64,0.96,1.4,0.96,2.29c0,0.84-0.3,1.57-0.9,2.21
c-0.6,0.63-1.33,0.97-2.17,1.02c-0.12,0-0.19,0.06-0.19,0.19v1.33c0,0.11,0.06,0.17,0.19,0.17c1.33-0.04,2.45-0.54,3.38-1.5
c0.93-0.96,1.39-2.09,1.39-3.41c0-0.76-0.14-1.43-0.43-2.03C22.6,13.95,23,12.85,23,11.6c0-0.94-0.23-1.81-0.7-2.61
c-0.47-0.8-1.11-1.44-1.91-1.91s-1.68-0.7-2.62-0.7c-1.54,0-2.83,0.58-3.87,1.73c-0.81-0.44-1.71-0.66-2.69-0.66
c-1.41,0-2.65,0.44-3.74,1.31s-1.78,1.99-2.09,3.34c-1.12,0.28-2.03,0.86-2.74,1.75C1.93,14.75,1.58,15.77,1.58,16.93z M7.92,20.98
c0,0.24,0.08,0.44,0.24,0.61c0.16,0.17,0.35,0.25,0.59,0.25c0.23,0,0.43-0.08,0.59-0.25c0.16-0.17,0.24-0.37,0.24-0.61
c0-0.23-0.08-0.42-0.24-0.58s-0.35-0.24-0.59-0.24c-0.23,0-0.43,0.08-0.59,0.24S7.92,20.76,7.92,20.98z M7.92,24.61
c0,0.21,0.08,0.4,0.24,0.57c0.18,0.16,0.37,0.24,0.58,0.24c0.24,0,0.43-0.08,0.59-0.23c0.16-0.16,0.23-0.35,0.23-0.58
c0-0.24-0.08-0.43-0.24-0.59c-0.16-0.16-0.35-0.23-0.59-0.23c-0.23,0-0.43,0.08-0.59,0.23C8,24.17,7.92,24.37,7.92,24.61z
M9.97,4.68c0,0.24,0.08,0.44,0.24,0.59l0.66,0.66c0.16,0.16,0.34,0.25,0.53,0.25c0.21,0.03,0.41-0.04,0.61-0.22
c0.2-0.18,0.3-0.39,0.3-0.63c0-0.24-0.08-0.46-0.24-0.64l-0.64-0.61c-0.15-0.17-0.34-0.25-0.58-0.25c-0.25,0-0.46,0.08-0.63,0.25
C10.05,4.24,9.97,4.44,9.97,4.68z M11.1,22.9c0,0.22,0.08,0.42,0.24,0.6c0.16,0.16,0.36,0.24,0.58,0.24c0.24,0,0.44-0.08,0.6-0.24
s0.25-0.36,0.25-0.6c0-0.23-0.08-0.43-0.25-0.6s-0.37-0.25-0.6-0.25c-0.23,0-0.42,0.08-0.58,0.25S11.1,22.67,11.1,22.9z M11.1,19.3
c0,0.23,0.08,0.42,0.24,0.58s0.36,0.24,0.58,0.24c0.24,0,0.44-0.08,0.6-0.24c0.17-0.16,0.25-0.35,0.25-0.59
c0-0.23-0.08-0.43-0.25-0.59s-0.37-0.24-0.6-0.24c-0.23,0-0.42,0.08-0.58,0.24S11.1,19.07,11.1,19.3z M11.1,26.56
c0,0.22,0.08,0.41,0.24,0.57c0.17,0.17,0.36,0.25,0.58,0.25c0.24,0,0.44-0.08,0.6-0.23c0.17-0.16,0.25-0.35,0.25-0.59
s-0.08-0.44-0.25-0.6c-0.17-0.17-0.37-0.25-0.6-0.25c-0.22,0-0.41,0.08-0.58,0.25C11.18,26.13,11.1,26.33,11.1,26.56z M14.32,20.98
c0,0.24,0.08,0.44,0.24,0.61c0.16,0.17,0.36,0.25,0.59,0.25s0.43-0.08,0.59-0.25c0.16-0.17,0.24-0.37,0.24-0.61
c0-0.23-0.08-0.42-0.24-0.58s-0.35-0.24-0.59-0.24s-0.43,0.08-0.59,0.24S14.32,20.76,14.32,20.98z M14.32,24.61
c0,0.21,0.08,0.4,0.23,0.57c0.18,0.16,0.38,0.24,0.6,0.24c0.24,0,0.43-0.08,0.59-0.23c0.16-0.16,0.23-0.35,0.23-0.58
c0-0.24-0.08-0.43-0.24-0.59c-0.16-0.16-0.35-0.23-0.59-0.23c-0.24,0-0.44,0.08-0.6,0.24C14.4,24.18,14.32,24.38,14.32,24.61z
M15.3,9.06c0.69-0.66,1.51-0.99,2.47-0.99c0.97,0,1.8,0.35,2.48,1.04c0.69,0.69,1.03,1.53,1.03,2.49c0,0.62-0.17,1.24-0.51,1.84
C19.82,12.48,18.66,12,17.3,12h-0.32C16.68,10.83,16.12,9.85,15.3,9.06z M16.9,3.84c0,0.23,0.08,0.43,0.25,0.58s0.37,0.23,0.61,0.23
s0.43-0.08,0.59-0.23c0.16-0.16,0.23-0.35,0.23-0.58V1.8c0-0.24-0.08-0.44-0.24-0.61S18,0.94,17.77,0.94s-0.43,0.09-0.6,0.26
c-0.17,0.17-0.26,0.37-0.26,0.6V3.84z M22.42,6.11c0,0.23,0.08,0.43,0.25,0.59c0.15,0.16,0.34,0.24,0.56,0.26s0.43-0.07,0.62-0.26
l1.43-1.43c0.18-0.18,0.26-0.38,0.26-0.61c0-0.24-0.09-0.44-0.26-0.61c-0.17-0.17-0.37-0.25-0.6-0.25c-0.22,0-0.41,0.08-0.58,0.25
l-1.43,1.46C22.5,5.67,22.42,5.87,22.42,6.11z M23.22,17.91c0,0.25,0.08,0.46,0.24,0.62l0.64,0.63c0.24,0.16,0.46,0.24,0.64,0.24
c0.21,0,0.39-0.09,0.56-0.26c0.17-0.17,0.25-0.38,0.25-0.61c0-0.23-0.09-0.42-0.26-0.58l-0.62-0.65c-0.18-0.16-0.38-0.24-0.61-0.24
s-0.43,0.08-0.59,0.25C23.3,17.47,23.22,17.67,23.22,17.91z M24.67,11.6c0,0.24,0.09,0.43,0.26,0.59c0.17,0.18,0.38,0.27,0.62,0.27
h2.02c0.23,0,0.43-0.08,0.6-0.25s0.25-0.37,0.25-0.61c0-0.24-0.08-0.44-0.25-0.6s-0.37-0.25-0.6-0.25h-2.02
c-0.24,0-0.44,0.08-0.62,0.25S24.67,11.37,24.67,11.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.49,16.88c0,1.12,0.33,2.12,1,3s1.53,1.47,2.58,1.76l-0.66,1.7c-0.05,0.14,0,0.22,0.14,0.22h2.13l-1.43,4.21h0.29
l4.36-5.66c0.04-0.04,0.04-0.09,0.02-0.14C9.9,21.92,9.85,21.9,9.78,21.9H7.59l2.49-4.65c0.07-0.14,0.03-0.22-0.14-0.22H6.98
c-0.09,0-0.17,0.05-0.23,0.15l-1.07,2.88C4.96,19.88,4.36,19.5,3.9,18.9c-0.47-0.59-0.7-1.26-0.7-2.02c0-0.84,0.28-1.57,0.84-2.18
c0.56-0.61,1.27-0.97,2.11-1.07l0.51-0.03c0.12,0,0.19-0.05,0.22-0.14l0.07-0.59c0.11-1.08,0.56-1.99,1.37-2.72s1.76-1.1,2.86-1.1
c1.09,0,2.04,0.37,2.86,1.1s1.29,1.64,1.4,2.72l0.08,0.59c0,0.11,0.06,0.17,0.18,0.17h1.61c0.89,0,1.66,0.32,2.31,0.96
s0.97,1.4,0.97,2.29c0,0.87-0.3,1.62-0.9,2.26s-1.32,0.98-2.18,1.03c-0.13,0-0.2,0.06-0.2,0.18v1.34c0,0.11,0.07,0.17,0.2,0.17
c0.88-0.02,1.69-0.26,2.42-0.72c0.73-0.45,1.31-1.06,1.74-1.81s0.64-1.57,0.64-2.45c0-0.73-0.14-1.4-0.43-2.02
c0.76-0.96,1.14-2.06,1.14-3.29c0-0.95-0.24-1.83-0.71-2.64c-0.47-0.81-1.11-1.45-1.92-1.92c-0.81-0.47-1.69-0.71-2.64-0.71
c-0.72,0-1.42,0.15-2.1,0.45c-0.68,0.3-1.26,0.72-1.76,1.25c-0.81-0.43-1.71-0.65-2.72-0.65c-1.42,0-2.68,0.44-3.77,1.32
s-1.8,2-2.1,3.37c-1.11,0.26-2.02,0.84-2.74,1.74C1.85,14.7,1.49,15.73,1.49,16.88z M9.67,26.8c0,0.15,0.05,0.31,0.16,0.47
c0.11,0.16,0.26,0.27,0.46,0.34c0.11,0.03,0.2,0.04,0.25,0.04c0.15,0,0.28-0.03,0.38-0.08c0.21-0.08,0.36-0.27,0.43-0.57l0.27-1.03
c0.06-0.25,0.03-0.47-0.08-0.67s-0.3-0.32-0.53-0.37c-0.21-0.07-0.41-0.04-0.62,0.07c-0.21,0.12-0.35,0.29-0.42,0.52l-0.25,1.04
C9.69,26.7,9.67,26.78,9.67,26.8z M9.9,4.59c0,0.23,0.08,0.43,0.25,0.6l0.65,0.66c0.16,0.16,0.34,0.24,0.55,0.26
c0.21,0.03,0.41-0.04,0.61-0.23c0.2-0.18,0.3-0.39,0.3-0.64c0-0.23-0.08-0.43-0.25-0.6l-0.63-0.66c-0.16-0.16-0.36-0.24-0.6-0.24
c-0.25,0-0.46,0.08-0.63,0.24C9.99,4.16,9.9,4.36,9.9,4.59z M11.01,22c-0.01,0.16,0.04,0.32,0.14,0.47c0.1,0.15,0.26,0.26,0.48,0.32
c0.21,0.07,0.42,0.05,0.62-0.06c0.2-0.11,0.34-0.3,0.42-0.56l0.3-1.03c0.07-0.22,0.04-0.43-0.08-0.63s-0.3-0.34-0.54-0.41
c-0.23-0.07-0.44-0.05-0.64,0.07c-0.2,0.12-0.34,0.29-0.41,0.53l-0.24,1.05C11.03,21.9,11.01,21.98,11.01,22z M13.84,23.68
c0,0.14,0.03,0.28,0.1,0.39c0.13,0.21,0.31,0.36,0.54,0.43c0.11,0.04,0.21,0.06,0.28,0.06c0.13,0,0.23-0.02,0.31-0.08
c0.2-0.07,0.35-0.27,0.45-0.6l0.25-1.01c0.07-0.24,0.05-0.45-0.07-0.65c-0.11-0.2-0.28-0.33-0.51-0.39
c-0.23-0.07-0.45-0.05-0.65,0.07c-0.2,0.11-0.34,0.28-0.41,0.51l-0.28,1.04C13.85,23.53,13.84,23.61,13.84,23.68z M15.21,18.86
c0,0.18,0.05,0.34,0.16,0.5c0.11,0.16,0.27,0.27,0.49,0.33c0.17,0.06,0.37,0.04,0.61-0.05c0.2-0.09,0.34-0.28,0.43-0.57l0.27-1
c0.06-0.25,0.04-0.47-0.08-0.67s-0.29-0.32-0.53-0.37c-0.23-0.07-0.44-0.05-0.64,0.06c-0.2,0.11-0.33,0.28-0.4,0.5l-0.29,1.06
C15.22,18.79,15.21,18.86,15.21,18.86z M15.31,9.02c0.67-0.64,1.48-0.97,2.45-0.97c0.98,0,1.82,0.34,2.51,1.03
c0.69,0.68,1.04,1.52,1.04,2.5c0,0.66-0.16,1.26-0.47,1.81c-0.96-0.96-2.13-1.44-3.52-1.44h-0.31C16.72,10.76,16.15,9.78,15.31,9.02
z M16.91,3.75c0,0.24,0.08,0.44,0.25,0.61s0.37,0.25,0.6,0.25c0.24,0,0.44-0.08,0.6-0.25c0.16-0.17,0.24-0.37,0.24-0.61V1.69
c0-0.24-0.08-0.45-0.24-0.61C18.2,0.91,18,0.82,17.76,0.82c-0.24,0-0.44,0.08-0.6,0.25s-0.25,0.37-0.25,0.61V3.75z M22.49,6.04
c0,0.24,0.08,0.44,0.23,0.6c0.14,0.16,0.32,0.24,0.55,0.26c0.23,0.02,0.44-0.07,0.63-0.26l1.44-1.44c0.18-0.16,0.26-0.36,0.26-0.6
s-0.09-0.44-0.26-0.6c-0.16-0.18-0.36-0.26-0.6-0.26c-0.23,0-0.42,0.09-0.58,0.26l-1.44,1.44C22.56,5.59,22.49,5.79,22.49,6.04z
M23.26,17.95c0,0.23,0.08,0.43,0.25,0.6l0.65,0.63c0.18,0.17,0.39,0.25,0.62,0.25l0.02,0.02c0.22,0,0.4-0.09,0.54-0.27
c0.18-0.16,0.26-0.36,0.26-0.6c0-0.23-0.09-0.43-0.26-0.61l-0.62-0.62c-0.18-0.18-0.38-0.27-0.61-0.27c-0.24,0-0.44,0.09-0.6,0.26
C23.35,17.51,23.26,17.72,23.26,17.95z M24.73,11.58c0,0.24,0.09,0.44,0.26,0.59c0.16,0.18,0.36,0.26,0.6,0.26h2.06
c0.24,0,0.44-0.08,0.61-0.25c0.17-0.17,0.25-0.37,0.25-0.6s-0.08-0.44-0.25-0.6c-0.17-0.16-0.37-0.24-0.61-0.24h-2.06
c-0.24,0-0.45,0.08-0.61,0.24C24.81,11.14,24.73,11.34,24.73,11.58z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M4.37,14.62c0-0.24,0.08-0.45,0.25-0.62c0.17-0.16,0.38-0.24,0.6-0.24h2.04c0.23,0,0.42,0.08,0.58,0.25
c0.15,0.17,0.23,0.37,0.23,0.61S8,15.06,7.85,15.23c-0.15,0.17-0.35,0.25-0.58,0.25H5.23c-0.23,0-0.43-0.08-0.6-0.25
C4.46,15.06,4.37,14.86,4.37,14.62z M7.23,21.55c0-0.23,0.08-0.43,0.23-0.61l1.47-1.43c0.15-0.16,0.35-0.23,0.59-0.23
c0.24,0,0.44,0.08,0.6,0.23s0.24,0.34,0.24,0.57c0,0.24-0.08,0.46-0.24,0.64L8.7,22.14c-0.41,0.32-0.82,0.32-1.23,0
C7.31,21.98,7.23,21.78,7.23,21.55z M7.23,7.71c0-0.23,0.08-0.43,0.23-0.61C7.66,6.93,7.87,6.85,8.1,6.85
c0.22,0,0.42,0.08,0.59,0.24l1.43,1.47c0.16,0.15,0.24,0.35,0.24,0.59c0,0.24-0.08,0.44-0.24,0.6s-0.36,0.24-0.6,0.24
c-0.24,0-0.44-0.08-0.59-0.24L7.47,8.32C7.31,8.16,7.23,7.95,7.23,7.71z M9.78,14.62c0-0.93,0.23-1.8,0.7-2.6s1.1-1.44,1.91-1.91
s1.67-0.7,2.6-0.7c0.7,0,1.37,0.14,2.02,0.42c0.64,0.28,1.2,0.65,1.66,1.12c0.47,0.47,0.84,1.02,1.11,1.66
c0.27,0.64,0.41,1.32,0.41,2.02c0,0.94-0.23,1.81-0.7,2.61c-0.47,0.8-1.1,1.43-1.9,1.9c-0.8,0.47-1.67,0.7-2.61,0.7
s-1.81-0.23-2.61-0.7c-0.8-0.47-1.43-1.1-1.9-1.9C10.02,16.43,9.78,15.56,9.78,14.62z M11.48,14.62c0,0.98,0.34,1.81,1.03,2.5
c0.68,0.69,1.51,1.04,2.49,1.04s1.81-0.35,2.5-1.04s1.04-1.52,1.04-2.5c0-0.96-0.35-1.78-1.04-2.47c-0.69-0.68-1.52-1.02-2.5-1.02
c-0.97,0-1.8,0.34-2.48,1.02C11.82,12.84,11.48,13.66,11.48,14.62z M14.14,22.4c0-0.24,0.08-0.44,0.25-0.6s0.37-0.24,0.6-0.24
c0.24,0,0.45,0.08,0.61,0.24s0.24,0.36,0.24,0.6v1.99c0,0.24-0.08,0.45-0.25,0.62c-0.17,0.17-0.37,0.25-0.6,0.25
s-0.44-0.08-0.6-0.25c-0.17-0.17-0.25-0.38-0.25-0.62V22.4z M14.14,6.9V4.86c0-0.23,0.08-0.43,0.25-0.6C14.56,4.09,14.76,4,15,4
s0.43,0.08,0.6,0.25c0.17,0.17,0.25,0.37,0.25,0.6V6.9c0,0.23-0.08,0.42-0.25,0.58S15.23,7.71,15,7.71s-0.44-0.08-0.6-0.23
S14.14,7.13,14.14,6.9z M19.66,20.08c0-0.23,0.08-0.42,0.23-0.56c0.15-0.16,0.34-0.23,0.56-0.23c0.24,0,0.44,0.08,0.6,0.23
l1.46,1.43c0.16,0.17,0.24,0.38,0.24,0.61c0,0.23-0.08,0.43-0.24,0.59c-0.4,0.31-0.8,0.31-1.2,0l-1.42-1.42
C19.74,20.55,19.66,20.34,19.66,20.08z M19.66,9.16c0-0.25,0.08-0.45,0.23-0.59l1.42-1.47c0.17-0.16,0.37-0.24,0.59-0.24
c0.24,0,0.44,0.08,0.6,0.25c0.17,0.17,0.25,0.37,0.25,0.6c0,0.25-0.08,0.46-0.24,0.62l-1.46,1.43c-0.18,0.16-0.38,0.24-0.6,0.24
c-0.23,0-0.41-0.08-0.56-0.24S19.66,9.4,19.66,9.16z M21.92,14.62c0-0.24,0.08-0.44,0.24-0.62c0.16-0.16,0.35-0.24,0.57-0.24h2.02
c0.23,0,0.43,0.09,0.6,0.26c0.17,0.17,0.26,0.37,0.26,0.6s-0.09,0.43-0.26,0.6c-0.17,0.17-0.37,0.25-0.6,0.25h-2.02
c-0.23,0-0.43-0.08-0.58-0.25S21.92,14.86,21.92,14.62z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M1.52,16.9c0,1.11,0.33,2.09,0.98,2.96s1.51,1.46,2.57,1.78l-0.64,1.7c-0.04,0.14,0,0.21,0.14,0.21H6.7L5.45,27.5h0.29
l4.17-5.39c0.04-0.04,0.04-0.09,0.01-0.14C9.9,21.92,9.85,21.9,9.78,21.9H7.61l2.47-4.63c0.07-0.14,0.02-0.22-0.14-0.22H7
c-0.09,0-0.17,0.05-0.23,0.14L5.7,20.07c-0.71-0.18-1.3-0.57-1.77-1.16c-0.47-0.59-0.7-1.26-0.7-2.01c0-0.83,0.28-1.55,0.85-2.17
s1.27-0.97,2.1-1.07L6.7,13.6c0.13,0,0.2-0.06,0.2-0.18l0.06-0.51c0.11-1.08,0.57-1.99,1.38-2.72c0.81-0.73,1.77-1.1,2.86-1.1
c1.09,0,2.04,0.37,2.85,1.1s1.28,1.64,1.4,2.72l0.06,0.58c0,0.11,0.06,0.17,0.18,0.17h1.61c0.91,0,1.68,0.32,2.32,0.95
c0.64,0.63,0.96,1.39,0.96,2.29c0,0.85-0.3,1.59-0.89,2.21c-0.59,0.62-1.32,0.97-2.19,1.04c-0.13,0-0.2,0.06-0.2,0.18v1.37
c0,0.11,0.07,0.17,0.2,0.17c1.33-0.04,2.46-0.55,3.39-1.51c0.93-0.96,1.39-2.11,1.39-3.45c0-0.74-0.14-1.41-0.43-2.01
c0.79-0.96,1.18-2.06,1.18-3.32c0-0.94-0.24-1.81-0.71-2.62c-0.47-0.81-1.11-1.45-1.92-1.92c-0.81-0.47-1.68-0.71-2.62-0.71
c-1.54,0-2.84,0.58-3.88,1.73c-0.81-0.43-1.71-0.65-2.7-0.65c-1.41,0-2.67,0.44-3.76,1.31s-1.79,1.99-2.1,3.36
c-1.11,0.26-2.02,0.83-2.73,1.73S1.52,15.75,1.52,16.9z M9.61,26.48c-0.01,0.15,0.03,0.3,0.14,0.44s0.26,0.25,0.46,0.33
c0.07,0.02,0.14,0.03,0.21,0.03c0.17,0,0.34-0.05,0.51-0.15s0.28-0.26,0.34-0.47l2.29-8.57c0.06-0.23,0.04-0.45-0.07-0.64
c-0.11-0.2-0.27-0.33-0.49-0.4c-0.23-0.07-0.45-0.05-0.65,0.07c-0.2,0.11-0.34,0.28-0.4,0.51l-2.31,8.6
C9.62,26.3,9.61,26.39,9.61,26.48z M9.94,4.63c0,0.24,0.08,0.43,0.25,0.59l0.64,0.66C11,6.05,11.2,6.13,11.44,6.14
c0.24,0,0.43-0.08,0.57-0.26c0.19-0.15,0.28-0.35,0.28-0.6c0-0.24-0.08-0.43-0.25-0.59l-0.63-0.66c-0.17-0.16-0.38-0.24-0.61-0.24
c-0.25,0-0.46,0.08-0.62,0.24C10.02,4.19,9.94,4.39,9.94,4.63z M13.77,23.43c0,0.12,0.04,0.24,0.11,0.38
c0.13,0.2,0.29,0.34,0.5,0.43c0.07,0.03,0.17,0.05,0.3,0.05c0.15,0,0.26-0.02,0.33-0.06c0.2-0.08,0.34-0.28,0.41-0.58l1.49-5.55
c0.06-0.24,0.04-0.45-0.07-0.65c-0.11-0.19-0.28-0.32-0.51-0.39c-0.23-0.07-0.45-0.05-0.64,0.07c-0.2,0.11-0.33,0.28-0.39,0.51
L13.8,23.2c0,0.02-0.01,0.06-0.02,0.11C13.77,23.37,13.77,23.4,13.77,23.43z M15.3,9.04c0.67-0.64,1.49-0.97,2.48-0.97
c0.97,0,1.81,0.34,2.5,1.02c0.69,0.68,1.04,1.51,1.04,2.48c0,0.62-0.17,1.24-0.52,1.85c-0.99-0.98-2.16-1.47-3.5-1.47h-0.31
C16.68,10.78,16.11,9.81,15.3,9.04z M16.91,3.79c0,0.23,0.09,0.43,0.26,0.6s0.37,0.26,0.6,0.26c0.24,0,0.43-0.08,0.59-0.25
c0.16-0.17,0.23-0.37,0.23-0.61V1.73c0-0.24-0.08-0.44-0.23-0.61s-0.35-0.25-0.59-0.25c-0.23,0-0.43,0.08-0.6,0.25
s-0.26,0.37-0.26,0.61V3.79z M22.44,6.07c0,0.24,0.09,0.44,0.26,0.6c0.14,0.17,0.33,0.25,0.57,0.25s0.44-0.08,0.6-0.25l1.44-1.45
c0.17-0.16,0.26-0.35,0.26-0.59c0-0.24-0.08-0.44-0.25-0.61c-0.17-0.17-0.37-0.25-0.61-0.25c-0.22,0-0.41,0.09-0.57,0.26L22.7,5.47
C22.53,5.63,22.44,5.83,22.44,6.07z M23.25,17.93c0,0.22,0.08,0.42,0.24,0.6l0.66,0.63c0.12,0.14,0.31,0.23,0.54,0.24l0.01,0.01
c0.01,0,0.03,0,0.05,0c0.02,0,0.03,0,0.05,0c0.19,0,0.36-0.09,0.53-0.26c0.17-0.16,0.26-0.36,0.26-0.6c0-0.23-0.09-0.43-0.26-0.61
l-0.65-0.61c-0.16-0.18-0.36-0.27-0.58-0.27c-0.23,0-0.43,0.08-0.6,0.25C23.33,17.49,23.25,17.7,23.25,17.93z M24.7,11.58
c0,0.23,0.09,0.43,0.27,0.6c0.18,0.18,0.38,0.27,0.61,0.27h2.03c0.23,0,0.43-0.09,0.6-0.26s0.26-0.38,0.26-0.61
c0-0.23-0.08-0.43-0.25-0.59c-0.17-0.16-0.37-0.24-0.61-0.24h-2.03c-0.25,0-0.46,0.08-0.63,0.24C24.78,11.15,24.7,11.35,24.7,11.58z
"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M7.96,24.51h0.39l6.88-10.18c0.09-0.18,0.04-0.27-0.15-0.27h-2.84l2.99-5.45c0.09-0.18,0.02-0.27-0.2-0.27h-3.81
c-0.11,0-0.2,0.06-0.29,0.18l-2.78,7.4c-0.02,0.18,0.04,0.27,0.19,0.27h2.75L7.96,24.51z M16.46,18.18h0.27l5.22-7.67
c0.05-0.08,0.06-0.15,0.04-0.2s-0.08-0.07-0.17-0.07h-2.1l2.18-4.03c0.12-0.2,0.06-0.3-0.18-0.3h-2.74c-0.13,0-0.23,0.06-0.3,0.19
l-2.08,5.48c-0.03,0.09-0.03,0.16,0.01,0.21c0.04,0.05,0.1,0.07,0.19,0.07h2.04L16.46,18.18z"/>
</svg>

After

Width:  |  Height:  |  Size: 806 B

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M4.14,16.9c0-1.16,0.35-2.18,1.06-3.08s1.62-1.47,2.74-1.72c0.23-1.03,0.7-1.93,1.4-2.7c0.7-0.77,1.55-1.32,2.53-1.65
c0.62-0.21,1.26-0.32,1.93-0.32c0.81,0,1.6,0.16,2.35,0.48c0.28-0.47,0.61-0.88,0.99-1.22c0.38-0.34,0.77-0.61,1.17-0.79
c0.4-0.18,0.8-0.32,1.18-0.41s0.76-0.13,1.12-0.13c0.38,0,0.79,0.05,1.23,0.16l0.82,0.25c0.14,0.06,0.18,0.13,0.14,0.22l-0.14,0.6
c-0.07,0.31-0.1,0.6-0.1,0.86c0,0.31,0.05,0.63,0.15,0.95c0.1,0.32,0.24,0.63,0.44,0.94c0.19,0.31,0.46,0.58,0.8,0.83
c0.34,0.25,0.72,0.44,1.15,0.57l0.62,0.22c0.1,0.03,0.15,0.08,0.15,0.16c0,0.02-0.01,0.04-0.02,0.07l-0.18,0.67
c-0.27,1.08-0.78,1.93-1.5,2.57c0.4,0.7,0.62,1.45,0.65,2.24c0.01,0.05,0.01,0.12,0.01,0.23c0,0.89-0.22,1.72-0.67,2.48
c-0.44,0.76-1.05,1.36-1.8,1.8c-0.76,0.44-1.59,0.67-2.48,0.67H9.07c-0.89,0-1.72-0.22-2.48-0.67s-1.35-1.05-1.79-1.8
S4.14,17.8,4.14,16.9z M5.85,16.9c0,0.89,0.32,1.66,0.96,2.31c0.64,0.65,1.39,0.98,2.26,0.98h10.81c0.89,0,1.65-0.32,2.28-0.97
s0.95-1.42,0.95-2.32c0-0.88-0.32-1.63-0.96-2.26c-0.64-0.63-1.4-0.95-2.28-0.95h-1.78l-0.1-0.75c-0.1-1.01-0.52-1.88-1.26-2.59
s-1.62-1.11-2.63-1.2c-0.03,0-0.08,0-0.15-0.01c-0.07-0.01-0.11-0.01-0.15-0.01c-0.51,0-1.02,0.1-1.54,0.29V9.4
c-0.73,0.28-1.35,0.74-1.84,1.37c-0.5,0.63-0.8,1.35-0.9,2.17l-0.07,0.72l-0.68,0.03c-0.84,0.1-1.54,0.45-2.1,1.06
S5.85,16.07,5.85,16.9z M17.6,8.79c1.06,0.91,1.72,1.97,1.97,3.18h0.32c1.24,0,2.3,0.39,3.17,1.18c0.33-0.31,0.58-0.67,0.76-1.07
c-0.91-0.43-1.63-1.09-2.16-1.97c-0.52-0.88-0.79-1.81-0.79-2.78V7.09c-0.05-0.01-0.13-0.01-0.24-0.01
c-0.58-0.01-1.15,0.13-1.7,0.44C18.38,7.82,17.93,8.24,17.6,8.79z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M4.12,16.91c0,1.33,0.46,2.48,1.39,3.43s2.06,1.47,3.4,1.53c0.11,0,0.17-0.06,0.17-0.17v-1.37c0-0.12-0.06-0.18-0.17-0.18
c-0.87-0.07-1.6-0.41-2.19-1.04c-0.59-0.62-0.89-1.36-0.89-2.21c0-0.84,0.28-1.57,0.85-2.19c0.57-0.62,1.26-0.97,2.1-1.04l0.53-0.07
c0.13,0,0.2-0.06,0.2-0.17l0.07-0.52c0.11-1.08,0.56-1.99,1.37-2.71c0.81-0.73,1.76-1.09,2.85-1.09c1.09,0,2.04,0.36,2.85,1.09
c0.81,0.72,1.27,1.63,1.39,2.72l0.07,0.58c0,0.12,0.06,0.18,0.18,0.18h1.61c0.91,0,1.68,0.32,2.32,0.95
c0.64,0.63,0.96,1.39,0.96,2.28c0,0.85-0.3,1.59-0.89,2.21c-0.59,0.62-1.32,0.97-2.19,1.04c-0.13,0-0.2,0.06-0.2,0.18v1.37
c0,0.11,0.07,0.17,0.2,0.17c1.33-0.04,2.46-0.55,3.38-1.51s1.38-2.11,1.38-3.45c0-0.89-0.23-1.72-0.68-2.48
c0.8-0.72,1.32-1.58,1.55-2.58l0.15-0.72c0.01-0.01,0.01-0.03,0.01-0.07c0-0.07-0.05-0.13-0.16-0.16l-0.58-0.17
c-0.57-0.16-1.05-0.44-1.45-0.82c-0.4-0.39-0.68-0.8-0.85-1.23c-0.17-0.43-0.25-0.87-0.25-1.32c0-0.24,0.03-0.51,0.09-0.79
l0.14-0.62c0.03-0.09-0.02-0.17-0.14-0.22l-0.79-0.24c-0.44-0.11-0.85-0.16-1.25-0.16c-0.36,0-0.73,0.04-1.12,0.13
c-0.38,0.09-0.78,0.22-1.19,0.41c-0.41,0.18-0.81,0.45-1.2,0.8c-0.39,0.35-0.72,0.75-1,1.22c-0.71-0.3-1.48-0.45-2.33-0.45
c-1.41,0-2.66,0.44-3.75,1.31c-1.09,0.87-1.79,1.99-2.1,3.35c-1.1,0.26-2.01,0.84-2.73,1.74C4.48,14.74,4.12,15.76,4.12,16.91z
M11.79,21.56c-0.05,0.14,0,0.22,0.14,0.22h2.16l-1.31,4.14h0.3l4.17-5.59c0.04-0.04,0.05-0.09,0.03-0.14
c-0.02-0.05-0.06-0.07-0.13-0.07h-2.2l2.31-4.21c0.07-0.14,0.02-0.22-0.14-0.22h-2.94c-0.08,0-0.15,0.05-0.22,0.14L11.79,21.56z
M17.6,8.81c0.33-0.57,0.77-1,1.33-1.3c0.55-0.3,1.13-0.45,1.72-0.45c0.13,0,0.22,0.01,0.27,0.02v0.31c0,0.96,0.26,1.87,0.78,2.73
c0.52,0.86,1.24,1.51,2.17,1.96c-0.16,0.37-0.41,0.73-0.75,1.07c-0.92-0.79-1.99-1.18-3.22-1.18h-0.32
C19.29,10.71,18.63,9.66,17.6,8.81z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<path d="M4.07,16.93c0,1.33,0.47,2.47,1.4,3.43s2.07,1.47,3.4,1.51c0.12,0,0.18-0.06,0.18-0.17v-1.34c0-0.11-0.06-0.17-0.18-0.17
c-0.85-0.04-1.58-0.39-2.18-1.03s-0.91-1.39-0.91-2.23c0-0.85,0.28-1.59,0.85-2.21c0.57-0.62,1.27-0.97,2.1-1.04l0.53-0.07
c0.13,0,0.2-0.06,0.2-0.18l0.07-0.51c0.11-1.1,0.56-2.02,1.37-2.75c0.81-0.73,1.76-1.1,2.86-1.1c1.09,0,2.04,0.37,2.86,1.1
c0.82,0.73,1.29,1.64,1.4,2.72l0.07,0.57c0,0.12,0.06,0.19,0.17,0.19h1.62c0.91,0,1.68,0.32,2.33,0.97
c0.65,0.64,0.97,1.41,0.97,2.31c0,0.55-0.14,1.07-0.41,1.56s-0.65,0.89-1.13,1.2c-0.48,0.31-1,0.48-1.56,0.51
c-0.13,0-0.2,0.06-0.2,0.17v1.34c0,0.11,0.07,0.17,0.2,0.17c0.88-0.02,1.69-0.26,2.42-0.71c0.73-0.45,1.31-1.05,1.73-1.8
c0.42-0.75,0.63-1.56,0.63-2.43c0-0.88-0.23-1.72-0.68-2.51c0.83-0.74,1.36-1.62,1.58-2.62l0.14-0.68c0.02-0.02,0.03-0.04,0.03-0.07
c0-0.06-0.06-0.11-0.17-0.16l-0.55-0.18c-0.57-0.17-1.07-0.45-1.47-0.85c-0.41-0.4-0.7-0.81-0.87-1.25
c-0.17-0.43-0.26-0.86-0.26-1.29c-0.02-0.21,0.01-0.49,0.09-0.82l0.13-0.58c0.04-0.1,0-0.18-0.13-0.23l-0.8-0.24
c-0.41-0.11-0.84-0.17-1.29-0.17c-0.36,0-0.74,0.04-1.12,0.13c-0.38,0.09-0.78,0.22-1.19,0.41s-0.81,0.46-1.2,0.81
c-0.39,0.35-0.72,0.76-1,1.23c-0.81-0.31-1.6-0.46-2.35-0.46c-1.41,0-2.67,0.44-3.76,1.32s-1.8,2-2.11,3.37
c-1.12,0.29-2.04,0.88-2.75,1.77C4.42,14.74,4.07,15.77,4.07,16.93z M9.48,23.98c0,0.17,0.05,0.34,0.16,0.51
c0.11,0.17,0.27,0.28,0.47,0.35c0.23,0.07,0.44,0.06,0.64-0.04s0.32-0.28,0.39-0.56l0.14-0.61c0.05-0.23,0.02-0.44-0.09-0.63
c-0.11-0.2-0.28-0.33-0.52-0.4c-0.23-0.07-0.44-0.04-0.64,0.08s-0.34,0.3-0.4,0.53L9.5,23.79C9.48,23.83,9.48,23.89,9.48,23.98z
M10.24,21.08c0,0.21,0.08,0.4,0.25,0.57c0.16,0.17,0.34,0.25,0.56,0.25c0.24,0,0.44-0.08,0.6-0.24c0.16-0.16,0.24-0.35,0.24-0.59
c0-0.23-0.08-0.43-0.24-0.59c-0.16-0.16-0.36-0.24-0.6-0.24c-0.23,0-0.43,0.08-0.58,0.23S10.24,20.85,10.24,21.08z M10.85,18.81
c-0.01,0.16,0.03,0.31,0.14,0.45c0.1,0.15,0.26,0.25,0.48,0.32c0.21,0.06,0.41,0.04,0.62-0.07c0.21-0.11,0.34-0.28,0.41-0.51
l0.28-0.9c0.07-0.24,0.05-0.46-0.07-0.65c-0.12-0.19-0.3-0.32-0.54-0.39c-0.22-0.07-0.43-0.05-0.63,0.07
c-0.2,0.11-0.34,0.28-0.41,0.5l-0.24,0.92c0,0.02-0.01,0.06-0.02,0.12C10.85,18.72,10.85,18.77,10.85,18.81z M12.01,27.1
c0,0.18,0.05,0.34,0.15,0.5c0.1,0.16,0.26,0.27,0.48,0.33c0.08,0.02,0.17,0.03,0.25,0.03c0.43,0,0.69-0.2,0.79-0.61l0.14-0.59
c0.06-0.26,0.03-0.48-0.08-0.68s-0.29-0.32-0.52-0.37c-0.21-0.07-0.42-0.05-0.63,0.07c-0.21,0.12-0.34,0.29-0.41,0.51l-0.14,0.59
C12.02,26.97,12.01,27.04,12.01,27.1z M12.79,24.2c0,0.22,0.08,0.41,0.25,0.58c0.16,0.16,0.35,0.24,0.57,0.24
c0.24,0,0.43-0.08,0.59-0.23c0.16-0.16,0.23-0.35,0.23-0.59c0-0.23-0.08-0.42-0.23-0.58s-0.35-0.23-0.59-0.23s-0.43,0.08-0.59,0.23
C12.87,23.77,12.79,23.97,12.79,24.2z M13.42,21.93c-0.01,0.15,0.03,0.31,0.13,0.47s0.25,0.26,0.45,0.3
c0.23,0.06,0.44,0.04,0.64-0.06c0.19-0.1,0.33-0.29,0.41-0.56l0.27-0.9c0.07-0.22,0.05-0.43-0.07-0.63c-0.12-0.2-0.29-0.33-0.53-0.4
c-0.22-0.07-0.43-0.04-0.64,0.08s-0.34,0.3-0.41,0.53l-0.22,0.9C13.43,21.74,13.42,21.83,13.42,21.93z M16.17,24.08
c0,0.16,0.05,0.32,0.15,0.48s0.26,0.27,0.46,0.33c0.03,0,0.08,0.01,0.14,0.02c0.06,0.01,0.1,0.02,0.14,0.02
c0.41,0,0.66-0.22,0.76-0.66l0.14-0.6c0.07-0.21,0.05-0.42-0.07-0.63c-0.11-0.21-0.28-0.34-0.51-0.41
c-0.25-0.06-0.48-0.04-0.68,0.08s-0.34,0.29-0.41,0.53l-0.09,0.59c0,0.02-0.01,0.07-0.02,0.12C16.18,24,16.17,24.04,16.17,24.08z
M16.91,21.12c0,0.22,0.08,0.42,0.25,0.57c0.15,0.16,0.34,0.24,0.57,0.24c0.24,0,0.43-0.08,0.59-0.23s0.23-0.35,0.23-0.58
c0-0.24-0.08-0.43-0.23-0.59s-0.35-0.23-0.59-0.23c-0.24,0-0.43,0.08-0.59,0.23S16.91,20.88,16.91,21.12z M17.52,18.81
c0,0.17,0.05,0.33,0.16,0.48s0.27,0.26,0.49,0.32c0.02,0,0.06,0.01,0.12,0.02c0.06,0.01,0.11,0.02,0.14,0.02
c0.1,0,0.22-0.03,0.36-0.09c0.21-0.11,0.35-0.29,0.41-0.52l0.24-0.9c0.06-0.23,0.04-0.44-0.08-0.63c-0.11-0.2-0.28-0.33-0.51-0.4
c-0.23-0.07-0.44-0.05-0.64,0.06s-0.32,0.27-0.39,0.51l-0.28,0.91c0,0.02-0.01,0.06-0.02,0.12C17.53,18.74,17.52,18.78,17.52,18.81z
M17.59,8.76c0.32-0.58,0.76-1.02,1.31-1.34c0.56-0.32,1.13-0.47,1.73-0.46c0.09,0,0.19,0.01,0.3,0.03V7.3
c-0.01,0.98,0.25,1.9,0.77,2.76c0.53,0.86,1.27,1.5,2.22,1.94c-0.19,0.41-0.46,0.78-0.8,1.11c-0.92-0.76-2-1.14-3.23-1.14h-0.31
C19.27,10.66,18.61,9.59,17.59,8.76z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

Some files were not shown because too many files have changed in this diff Show More