{"id":368307,"date":"2024-05-21T03:39:55","date_gmt":"2024-05-21T03:39:55","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=368307"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=368307","title":{"rendered":"<span>\u041f\u043e\u043c\u043e\u0449\u044c \u043b\u043e\u0433\u0438\u0441\u0442\u0430\u043c: \u043a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0435 \u0442\u043e\u0432\u0430\u0440\u0430 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434\u0435 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 FastAPI<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0410\u043d\u0430\u0441\u0442\u0430\u0441\u0438\u044f \u0418\u0432\u0430\u043d\u043e\u0432\u0430, \u044f \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0438\u0441\u0430\u0442\u0435\u043b\u044c \u041c\u0422\u0421 Exolve. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e FastAPI. \u041e\u043d\u0430 \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u0435\u0442 \u043b\u043e\u0433\u0438\u0441\u0442\u043e\u0432 \u043e \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\u00a0 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 <a href=\"https:\/\/exolve.ru\/products\/sms-api\/?utm_source=habr&amp;utm_medium=refferal&amp;utm_campaign=sms_api_article&amp;utm_content=article_sklad&amp;utm_term=sms_api_exolve\">\u041c\u0422\u0421 Exolve<\/a>, \u043d\u0430 \u0435\u0451 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u043e\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u2014 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/d21\/a1d\/4b8\/d21a1d4b8b22dd1a3d63f6eb932b471a.jpg\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d21\/a1d\/4b8\/d21a1d4b8b22dd1a3d63f6eb932b471a.jpg\" data-blurred=\"true\"\/><\/figure>\n<h3>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0435\u0434\u0438\u043d\u044b\u0439 \u0441\u043a\u043b\u0430\u0434 \u0438 \u0441\u0435\u043a\u0442\u043e\u0440\u044b \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438, \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u043b\u0430\u0442\u0438\u043d\u0441\u043a\u0438\u043c\u0438 \u0431\u0443\u043a\u0432\u0430\u043c\u0438. \u041d\u0443\u0436\u043d\u043e \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u0442\u044c \u0433\u0440\u0443\u0437\u0447\u0438\u043a\u043e\u0432 \u043e \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044f \u0432 \u0441\u0435\u043a\u0442\u043e\u0440 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438. \u042d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u044b \u0438 \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0448\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u0431\u0435\u0434\u0438\u043c\u0441\u044f, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c FastAPI \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, Redis \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 FastAPI Cache \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<pre><code>smsfastapi\/     \/venv     \/example_db         __init__.py         shemas.py         infodb.py     \/router         __init__.py         stock.py         worker.py     config.py     main.py     dev.env<\/code><\/pre>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>config.py<\/strong> \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a API-\u043a\u043b\u044e\u0447 \u0438 \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 SMS. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 Redis \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <strong>&#171;startup&#187;<\/strong> \u0438 \u0440\u0430\u0437\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0432 &#171;<strong>shutdown&#187;<\/strong> \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>main.py<\/strong>:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>from fastapi import FastAPI from fastapi_cache import FastAPICache from fastapi.responses import HTMLResponse from redis import asyncio as aioredis from fastapi_cache.backends.redis import RedisBackend import uvicorn from fastapi.middleware.cors import CORSMiddleware from router import stock, worker   origins = [ \"http:\/\/localhost:3000\", \"http:\/\/localhost:5173\", \"http:\/\/localhost:8000\" ]   app = FastAPI(title='Rule of Stock',           description='API \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439, \u043f\u0440\u0438\u0431\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434. \\n\\n')   app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=[\"GET\", \"POST\", \"OPTIONS\", \"DELETE\", \"PATCH\", \"PUT\"], allow_headers=[\"Content-Type\", \"Set-Cookie\", \"Access-Control-Allow-Headers\", \"Access-Control-Allow-Origin\",                \"Authorization\"], )   @app.get(\"\/\", response_class=HTMLResponse) async def start(): \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 Swagger \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f API.\\n \"\"\" return \"\"\"     &lt;html>         &lt;head>             &lt;title>Rule of Stock&lt;\/title>             &lt;style>                 body {                     background-color: white;                     font-family: Arial, sans-serif;                     text-align: center;                 }                 h1 {margin-top: 80px;}             &lt;\/style>         &lt;\/head>         &lt;body>             &lt;h3>API \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439, \u043f\u0440\u0438\u0431\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434&lt;\/h3> &lt;p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b API \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 &lt;a href=\"http:\/\/0.0.0.0:8000\/docs\">\u0437\u0434\u0435\u0441\u044c&lt;\/a>.&lt;\/p>&lt;p>-------------------------------------------------------------&lt;\/p>         &lt;\/body>     &lt;\/html>     \"\"\"  @app.on_event(\"startup\") async def startup(): redis = await aioredis.from_url(\"redis:\/\/localhost:6379\", encoding=\"utf8\", decode_responses=True) cache_redis = FastAPICache.init(RedisBackend(redis), prefix=\"stock_cache\") return cache_redis   @app.on_event(\"shutdown\") async def shutdown(): backend = FastAPICache.get_backend() if isinstance(backend, RedisBackend):     redis = backend.redis     await redis.close()<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439 \u0438 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0430. \u041d\u0430\u0448\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0441\u0445\u0435\u043c\u043e\u0439 BaseAuto, \u0430 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0438 \u2014 BaseWorker \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Pydantic:<\/p>\n<pre><code>from pydantic import BaseModel, EmailStr, Field, validator  class BaseAuto(BaseModel): number: str name: str  class BaseWorker(BaseModel): name: str is_work: bool is_free: bool<\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u043a\u043b\u0430\u0434\u043e\u0432 \u0441 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438 stocks_list <strong>=<\/strong> [{&#8216;name&#8217;:&#8217;A&#8217;, &#8216;is_free&#8217;: <strong>True<\/strong>},<br \/> {&#8216;name&#8217;:&#8217;B&#8217;, &#8216;is_free&#8217;: <strong>True<\/strong>},\u2026\u2026]<\/p>\n<p>\u0438 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432:<\/p>\n<pre><code>workers_list = {  'Ivan': {'is_work': True,  'stocks': ['A', 'B', 'C', 'D'],  'is_free': True,  'phone': '79801331100'},  'Roman': {'is_work': True,  'stocks': ['E', 'F', 'G', 'H'],  'is_free': True,  'phone': '79801034411'},\u2026.}<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/pastebin.com\/ufsfnRya\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>config.py<\/strong> \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code>from dotenv import dotenv_values  info_env = dotenv_values('dev.env')  API_KEY = info_env.get('API_KEY') PHONE_SEND = info_env.get('PHONE_SEND') <\/code><\/pre>\n<h3>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430\u043c\u0438<\/h3>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>__init__.py<\/strong>. \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 <strong>\/router<\/strong> \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u043c\u0438:<\/p>\n<pre><code>from fastapi import APIRouter import aiohttp from fastapi_cache.decorator import cache from fastapi import FastAPI, HTTPException<\/code><\/pre>\n<p>\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430\u043c\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>worker.py<\/strong>. \u0414\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 <strong>FastAPI Cache<\/strong>. \u042d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0443\u0441\u043a\u043e\u0440\u0438\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u043c \u0434\u0430\u043d\u043d\u044b\u043c \u043e \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430\u0445.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>from .__init__ import * from config import API_KEY, PHONE_SEND from example_db import (infodb, shemas)  routework = APIRouter(prefix=\"\/worker\", tags=['Worker'])  @routework.get('\/read') @cache(expire=30, namespace='personal') async def read_workers(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\\n \"\"\" result =  [{worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items()][skip:limit] return result @routework.get('\/free') @cache(expire=30, namespace='personal') async def free_workers(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0445 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\\n \"\"\" result = [{worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items() if worker_data['is_free']][skip:limit] return result  @routework.get('\/busy') @cache(expire=30, namespace='personal') async def busy_workers(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043d\u044f\u0442\u044b\u0445 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\\n \"\"\" result = [{worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items() if not worker_data['is_free']][skip:limit] return result<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441\u043e \u0441\u043a\u043b\u0430\u0434\u0430\u043c\u0438<\/h3>\n<p>\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0441\u043a\u043b\u0430\u0434\u0430\u043c\u0438 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b\u0435 stock.py. \u0418 \u043c\u044b \u0441\u043d\u043e\u0432\u0430 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>from .__init__ import *  from example_db import (infodb, shemas)  routestock = APIRouter(prefix=\"\/stock\", tags=['Stock'])  @routestock.get('\/read') @cache(expire=30, namespace='stock') async def read_stock(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0441\u0442\u043e\u0432.\\n \"\"\" result = infodb.stocks_list[skip:limit] return result   @routestock.get('\/free') @cache(expire=30, namespace='stock') async def free_stock(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0445 \u0441\u043a\u043b\u0430\u0434\u043e\u0432.\\n \"\"\" result = [stock_busy for stock_busy in infodb.stocks_list if stock_busy[\"is_free\"]][skip:limit] return result   @routestock.get('\/busy') @cache(expire=30, namespace='stock') async def busy_stock(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043d\u044f\u0442\u044b\u0445 \u0441\u043a\u043b\u0430\u0434\u043e\u0432.\\n \"\"\" result = [stock_busy for stock_busy in infodb.stocks_list if not stock_busy[\"is_free\"]][skip:limit] return result<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 SMS<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044c \u043f\u0440\u0438\u0435\u0437\u0436\u0430\u0435\u0442 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434, \u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c \u043f\u0440\u0438\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u043a \u0432\u043e\u0440\u043e\u0442\u0430\u043c. \u0412 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044f SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <a href=\"https:\/\/exolve.ru\/products\/sms-api\/?utm_source=habr&amp;utm_medium=refferal&amp;utm_campaign=sms_api_article&amp;utm_content=article_sklad&amp;utm_term=sms_api_exolve\">\u041c\u0422\u0421 Exolve<\/a>. <\/p>\n<h4>\u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 aiohttp <\/h4>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0430\u0441\u043f\u0435\u043a\u0442\u043e\u0432 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u2014 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 <a href=\"https:\/\/exolve.ru\/products\/sms-api\/?utm_source=habr&amp;utm_medium=refferal&amp;utm_campaign=sms_api_article&amp;utm_content=article_sklad&amp;utm_term=sms_api_exolve\">\u041c\u0422\u0421 Exolve<\/a>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 aiohttp, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<p>\u0412\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>@routework.post(\"\/{stock}\/message\") async def send_sms(stock: str, info_auto: shemas.BaseAuto):     \"\"\"     \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n     \u041d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044f \u0438 \u043e\u043f\u043e\u0432\u0435\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u043e SMS.\\n     \"\"\"     url = \"https:\/\/api.exolve.ru\/messaging\/v1\/SendSMS\"     headers = {\"Authorization\": f\"Bearer {API_KEY}\"}          # \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u044e \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043a \u0441\u043a\u043b\u0430\u0434\u0443 \u0438 \u0437\u0430\u043d\u044f\u0442\u043e\u0441\u0442\u0438     worker_free = next(({worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items() if (stock in worker_data['stocks'] and worker_data['is_free'])), None)     worker_name = list(worker_free.keys())[0]         # \u041c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u043a\u0442\u043e\u0440\u0430 \u043d\u0430 \u00ab\u0437\u0430\u043d\u044f\u0442\u044b\u0439\u00bb     for elem in infodb.stocks_list:         if elem[\"name\"] == stock: elem[\"is_free\"] = False          # \u041c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u043d\u0430 \u00ab\u0437\u0430\u043d\u044f\u0442\u044b\u0439\u00bb     infodb.workers_list[worker_name][\"is_free\"] = False          sms_data = {         \"number\": PHONE_SEND,         \"destination\": worker_free[worker_name][\"phone\"],         \"text\": f\"\u0410\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044c {info_auto.number} \u043f\u0440\u0438\u0431\u044b\u043b \u043d\u0430 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0443 \u0432 \u0441\u0435\u043a\u0442\u043e\u0440 {stock}\"     }          # \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e     async with aiohttp.ClientSession() as session:         async with session.post(url, headers=headers, json=sms_data) as response:             response_data = await response.json()             return response_data<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h4>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u0435\u0441\u0441\u0438\u0439<\/h4>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432 \u044d\u0442\u043e\u043c \u043a\u043e\u0434\u0435 \u2014 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0441\u0435\u0441\u0441\u0438\u044f aiohttp.ClientSession. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u043d\u043e \u0434\u043b\u044f \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<h4>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430<\/h4>\n<p>\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL, \u0442\u043e \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0435\u0439 \u043d\u0443\u0436\u0435\u043d \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0434\u0440\u0430\u0439\u0432\u0435\u0440, \u0442\u0430\u043a\u043e\u0439 \u043a\u0430\u043a asyncpg. \u0422\u0430\u043a \u043c\u044b \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u043c \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0443 \u0441\u043e\u043a\u0435\u0442\u0430 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>\u0422\u0430\u043a\u043e\u043c\u0443 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u043d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0432\u0441\u0435\u0445 \u044d\u0442\u0430\u043f\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a \u043f\u0440\u0438\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u0432 \u0441\u0435\u043a\u0442\u043e\u0440\u0435, \u0433\u0434\u0435 \u043e\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f. \u0422\u0430\u043a \u043e\u043d \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u0442 \u0441\u0438\u0433\u043d\u0430\u043b \u043d\u0430 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0432 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b <strong>stock.py<\/strong>:<\/p>\n<pre><code>@routestock.post(\"\/{stock}\/change\") async def end_load(stock: str, info_worker: shemas.BaseWorker): \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u043a\u0442\u043e\u0440\u0430 \u0438 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0430 \u043d\u0430 \u00ab\u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0439\u00bb.\\n \"\"\" # \u043c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u043a\u0442\u043e\u0440\u0430 \u043d\u0430 \u00ab\u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0439\u00bb for elem in infodb.stocks_list:     if elem[\"name\"] == stock: elem[\"is_free\"] = True # \u043c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0430 \u043d\u0430 \u00ab\u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0439\u00bb infodb.workers_list[info_worker.name][\u201cis_free\u201d] = True return infodb.workers_list[info_worker.name]<\/code><\/pre>\n<h4>\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0438 \u043d\u0430\u0447\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0434\u043d\u044f<\/h4>\n<p>\u0420\u0430\u0431\u043e\u0447\u0438\u0439 \u0434\u0435\u043d\u044c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0438 \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043e\u0432. \u0421\u0438\u0433\u043d\u0430\u043b \u043e\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 API. \u042d\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u0445:<\/p>\n<pre><code>@routework.post(\"\/finish\") async def end_work(info_worker: shemas.BaseWorker)-> dict: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u0421\u0438\u0433\u043d\u0430\u043b \u043e\u0442 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430. \u041a\u043e\u043d\u0435\u0446 \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0434\u043d\u044f.\\n \"\"\" infodb.workers_list[info_worker.name][\u201dis_free\u201d] = False infodb.workers_list[info_worker.name][\u201dis_work\u201d] = False return infodb.workers_list[info_worker.name]   @routework.post(\"\/workday\") async def start_work(info_worker: shemas.BaseWorker)-> dict: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u0421\u0438\u0433\u043d\u0430\u043b \u043e\u0442 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430. \u041d\u0430\u0447\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0434\u043d\u044f.\\n \"\"\" infodb.workers_list[info_worker.name][\u201dis_free\u201d] = True infodb.workers_list[info_worker.name][\u201dis_work\u201d] = True return infodb.workers_list[info_worker.name]<\/code><\/pre>\n<h4>\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u0433\u043b\u0430\u0432\u043d\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u043c main.py \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438 \u0441\u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432 \u0438 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430:<\/p>\n<pre><code>app.include_router(stock.routestock) app.include_router(worker.routework)  if __name__ == \"__main__\": uvicorn.run(\"main:app\", port=8000, host=\"0.0.0.0\", reload=True, workers=4)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f API \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0437 \u0441\u0440\u0435\u0434\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"http:\/\/0.0.0.0:8000\/docs#\/\"><em>http:\/\/0.0.0.0:8000\/docs#\/<\/em><\/a><em>. <\/em>FastAPI \u0441\u0440\u0430\u0437\u0443 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u0443\u044e \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e Swagger, \u0441 \u0435\u0451 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c FastAPI, Redis \u0438 aiohttp \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u044b SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438. \u041e\u043d \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0432\u0430\u0436\u043d\u0430 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 API.<\/p>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439. \u041d\u0430 \u0431\u0430\u0437\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u0441 \u0431\u0430\u0437\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b<\/h3>\n<ul>\n<li>\n<p><a href=\"https:\/\/fastapi.tiangolo.com\/\">FastAPI Documentation<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/redis.io\/\">Redis<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/arbuz-team\/fastapi-cache\">FastAPI Cache<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.aiohttp.org\/\">aiohttp<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/793960\/\"> https:\/\/habr.com\/ru\/articles\/793960\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0410\u043d\u0430\u0441\u0442\u0430\u0441\u0438\u044f \u0418\u0432\u0430\u043d\u043e\u0432\u0430, \u044f \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0438\u0441\u0430\u0442\u0435\u043b\u044c \u041c\u0422\u0421 Exolve. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e FastAPI. \u041e\u043d\u0430 \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u0435\u0442 \u043b\u043e\u0433\u0438\u0441\u0442\u043e\u0432 \u043e \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\u00a0 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439 <a href=\"https:\/\/exolve.ru\/products\/sms-api\/?utm_source=habr&amp;utm_medium=refferal&amp;utm_campaign=sms_api_article&amp;utm_content=article_sklad&amp;utm_term=sms_api_exolve\">\u041c\u0422\u0421 Exolve<\/a>, \u043d\u0430 \u0435\u0451 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u043e\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u2014 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c. <\/p>\n<figure class=\"full-width\"><\/figure>\n<h3>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0435\u0434\u0438\u043d\u044b\u0439 \u0441\u043a\u043b\u0430\u0434 \u0438 \u0441\u0435\u043a\u0442\u043e\u0440\u044b \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438, \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u043b\u0430\u0442\u0438\u043d\u0441\u043a\u0438\u043c\u0438 \u0431\u0443\u043a\u0432\u0430\u043c\u0438. \u041d\u0443\u0436\u043d\u043e \u043e\u043f\u043e\u0432\u0435\u0449\u0430\u0442\u044c \u0433\u0440\u0443\u0437\u0447\u0438\u043a\u043e\u0432 \u043e \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044f \u0432 \u0441\u0435\u043a\u0442\u043e\u0440 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438. \u042d\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u044b \u0438 \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0448\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u0431\u0435\u0434\u0438\u043c\u0441\u044f, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c FastAPI \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, Redis \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 FastAPI Cache \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<pre><code>smsfastapi\/     \/venv     \/example_db         __init__.py         shemas.py         infodb.py     \/router         __init__.py         stock.py         worker.py     config.py     main.py     dev.env<\/code><\/pre>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>config.py<\/strong> \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a API-\u043a\u043b\u044e\u0447 \u0438 \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 SMS. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 Redis \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <strong>&#171;startup&#187;<\/strong> \u0438 \u0440\u0430\u0437\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0432 &#171;<strong>shutdown&#187;<\/strong> \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>main.py<\/strong>:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>from fastapi import FastAPI from fastapi_cache import FastAPICache from fastapi.responses import HTMLResponse from redis import asyncio as aioredis from fastapi_cache.backends.redis import RedisBackend import uvicorn from fastapi.middleware.cors import CORSMiddleware from router import stock, worker   origins = [ \"http:\/\/localhost:3000\", \"http:\/\/localhost:5173\", \"http:\/\/localhost:8000\" ]   app = FastAPI(title='Rule of Stock',           description='API \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439, \u043f\u0440\u0438\u0431\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434. \\n\\n')   app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=[\"GET\", \"POST\", \"OPTIONS\", \"DELETE\", \"PATCH\", \"PUT\"], allow_headers=[\"Content-Type\", \"Set-Cookie\", \"Access-Control-Allow-Headers\", \"Access-Control-Allow-Origin\",                \"Authorization\"], )   @app.get(\"\/\", response_class=HTMLResponse) async def start(): \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 Swagger \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f API.\\n \"\"\" return \"\"\"     &lt;html>         &lt;head>             &lt;title>Rule of Stock&lt;\/title>             &lt;style>                 body {                     background-color: white;                     font-family: Arial, sans-serif;                     text-align: center;                 }                 h1 {margin-top: 80px;}             &lt;\/style>         &lt;\/head>         &lt;body>             &lt;h3>API \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439, \u043f\u0440\u0438\u0431\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434&lt;\/h3> &lt;p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0440\u0430\u0431\u043e\u0442\u044b API \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 &lt;a href=\"http:\/\/0.0.0.0:8000\/docs\">\u0437\u0434\u0435\u0441\u044c&lt;\/a>.&lt;\/p>&lt;p>-------------------------------------------------------------&lt;\/p>         &lt;\/body>     &lt;\/html>     \"\"\"  @app.on_event(\"startup\") async def startup(): redis = await aioredis.from_url(\"redis:\/\/localhost:6379\", encoding=\"utf8\", decode_responses=True) cache_redis = FastAPICache.init(RedisBackend(redis), prefix=\"stock_cache\") return cache_redis   @app.on_event(\"shutdown\") async def shutdown(): backend = FastAPICache.get_backend() if isinstance(backend, RedisBackend):     redis = backend.redis     await redis.close()<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439 \u0438 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0430. \u041d\u0430\u0448\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0441\u0445\u0435\u043c\u043e\u0439 BaseAuto, \u0430 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0438 \u2014 BaseWorker \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Pydantic:<\/p>\n<pre><code>from pydantic import BaseModel, EmailStr, Field, validator  class BaseAuto(BaseModel): number: str name: str  class BaseWorker(BaseModel): name: str is_work: bool is_free: bool<\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u043a\u043b\u0430\u0434\u043e\u0432 \u0441 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438 stocks_list <strong>=<\/strong> [{&#8216;name&#8217;:&#8217;A&#8217;, &#8216;is_free&#8217;: <strong>True<\/strong>},<br \/> {&#8216;name&#8217;:&#8217;B&#8217;, &#8216;is_free&#8217;: <strong>True<\/strong>},\u2026\u2026]<\/p>\n<p>\u0438 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432:<\/p>\n<pre><code>workers_list = {  'Ivan': {'is_work': True,  'stocks': ['A', 'B', 'C', 'D'],  'is_free': True,  'phone': '79801331100'},  'Roman': {'is_work': True,  'stocks': ['E', 'F', 'G', 'H'],  'is_free': True,  'phone': '79801034411'},\u2026.}<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/pastebin.com\/ufsfnRya\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>config.py<\/strong> \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code>from dotenv import dotenv_values  info_env = dotenv_values('dev.env')  API_KEY = info_env.get('API_KEY') PHONE_SEND = info_env.get('PHONE_SEND') <\/code><\/pre>\n<h3>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430\u043c\u0438<\/h3>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <strong>__init__.py<\/strong>. \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 <strong>\/router<\/strong> \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430\u043c\u0438:<\/p>\n<pre><code>from fastapi import APIRouter import aiohttp from fastapi_cache.decorator import cache from fastapi import FastAPI, HTTPException<\/code><\/pre>\n<p>\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430\u043c\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>worker.py<\/strong>. \u0414\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 <strong>FastAPI Cache<\/strong>. \u042d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0443\u0441\u043a\u043e\u0440\u0438\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u043c \u0434\u0430\u043d\u043d\u044b\u043c \u043e \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430\u0445.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>from .__init__ import * from config import API_KEY, PHONE_SEND from example_db import (infodb, shemas)  routework = APIRouter(prefix=\"\/worker\", tags=['Worker'])  @routework.get('\/read') @cache(expire=30, namespace='personal') async def read_workers(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\\n \"\"\" result =  [{worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items()][skip:limit] return result @routework.get('\/free') @cache(expire=30, namespace='personal') async def free_workers(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0445 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\\n \"\"\" result = [{worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items() if worker_data['is_free']][skip:limit] return result  @routework.get('\/busy') @cache(expire=30, namespace='personal') async def busy_workers(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043d\u044f\u0442\u044b\u0445 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u043e\u0432.\\n \"\"\" result = [{worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items() if not worker_data['is_free']][skip:limit] return result<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441\u043e \u0441\u043a\u043b\u0430\u0434\u0430\u043c\u0438<\/h3>\n<p>\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0441\u043a\u043b\u0430\u0434\u0430\u043c\u0438 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b\u0435 stock.py. \u0418 \u043c\u044b \u0441\u043d\u043e\u0432\u0430 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>from .__init__ import *  from example_db import (infodb, shemas)  routestock = APIRouter(prefix=\"\/stock\", tags=['Stock'])  @routestock.get('\/read') @cache(expire=30, namespace='stock') async def read_stock(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0441\u0442\u043e\u0432.\\n \"\"\" result = infodb.stocks_list[skip:limit] return result   @routestock.get('\/free') @cache(expire=30, namespace='stock') async def free_stock(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0445 \u0441\u043a\u043b\u0430\u0434\u043e\u0432.\\n \"\"\" result = [stock_busy for stock_busy in infodb.stocks_list if stock_busy[\"is_free\"]][skip:limit] return result   @routestock.get('\/busy') @cache(expire=30, namespace='stock') async def busy_stock(skip: int = 0, limit: int = 100,) -> list: \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u043d\u044f\u0442\u044b\u0445 \u0441\u043a\u043b\u0430\u0434\u043e\u0432.\\n \"\"\" result = [stock_busy for stock_busy in infodb.stocks_list if not stock_busy[\"is_free\"]][skip:limit] return result<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 SMS<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044c \u043f\u0440\u0438\u0435\u0437\u0436\u0430\u0435\u0442 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434, \u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c \u043f\u0440\u0438\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u043a \u0432\u043e\u0440\u043e\u0442\u0430\u043c. \u0412 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044f SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <a href=\"https:\/\/exolve.ru\/products\/sms-api\/?utm_source=habr&amp;utm_medium=refferal&amp;utm_campaign=sms_api_article&amp;utm_content=article_sklad&amp;utm_term=sms_api_exolve\">\u041c\u0422\u0421 Exolve<\/a>. <\/p>\n<h4>\u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 aiohttp <\/h4>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0430\u0441\u043f\u0435\u043a\u0442\u043e\u0432 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u2014 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 <a href=\"https:\/\/exolve.ru\/products\/sms-api\/?utm_source=habr&amp;utm_medium=refferal&amp;utm_campaign=sms_api_article&amp;utm_content=article_sklad&amp;utm_term=sms_api_exolve\">\u041c\u0422\u0421 Exolve<\/a>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 aiohttp, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<p>\u0412\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>@routework.post(\"\/{stock}\/message\") async def send_sms(stock: str, info_auto: shemas.BaseAuto):     \"\"\"     \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n     \u041d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044f \u0438 \u043e\u043f\u043e\u0432\u0435\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u043e SMS.\\n     \"\"\"     url = \"https:\/\/api.exolve.ru\/messaging\/v1\/SendSMS\"     headers = {\"Authorization\": f\"Bearer {API_KEY}\"}          # \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u044e \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u043a \u0441\u043a\u043b\u0430\u0434\u0443 \u0438 \u0437\u0430\u043d\u044f\u0442\u043e\u0441\u0442\u0438     worker_free = next(({worker_name: worker_data} for worker_name, worker_data in infodb.workers_list.items() if (stock in worker_data['stocks'] and worker_data['is_free'])), None)     worker_name = list(worker_free.keys())[0]         # \u041c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u043a\u0442\u043e\u0440\u0430 \u043d\u0430 \u00ab\u0437\u0430\u043d\u044f\u0442\u044b\u0439\u00bb     for elem in infodb.stocks_list:         if elem[\"name\"] == stock: elem[\"is_free\"] = False          # \u041c\u0435\u043d\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a\u0430 \u043d\u0430 \u00ab\u0437\u0430\u043d\u044f\u0442\u044b\u0439\u00bb     infodb.workers_list[worker_name][\"is_free\"] = False          sms_data = {         \"number\": PHONE_SEND,         \"destination\": worker_free[worker_name][\"phone\"],         \"text\": f\"\u0410\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044c {info_auto.number} \u043f\u0440\u0438\u0431\u044b\u043b \u043d\u0430 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0443 \u0432 \u0441\u0435\u043a\u0442\u043e\u0440 {stock}\"     }          # \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e     async with aiohttp.ClientSession() as session:         async with session.post(url, headers=headers, json=sms_data) as response:             response_data = await response.json()             return response_data<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h4>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u0435\u0441\u0441\u0438\u0439<\/h4>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432 \u044d\u0442\u043e\u043c \u043a\u043e\u0434\u0435 \u2014 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0441\u0435\u0441\u0441\u0438\u044f aiohttp.ClientSession. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u043e\u0442\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u043d\u043e \u0434\u043b\u044f \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<h4>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430<\/h4>\n<p>\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL, \u0442\u043e \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0435\u0439 \u043d\u0443\u0436\u0435\u043d \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0434\u0440\u0430\u0439\u0432\u0435\u0440, \u0442\u0430\u043a\u043e\u0439 \u043a\u0430\u043a asyncpg. \u0422\u0430\u043a \u043c\u044b \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u043c \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0443 \u0441\u043e\u043a\u0435\u0442\u0430 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u043c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>\u0422\u0430\u043a\u043e\u043c\u0443 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u043d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0432\u0441\u0435\u0445 \u044d\u0442\u0430\u043f\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0438<\/h3>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0440\u0430\u0437\u0433\u0440\u0443\u0437\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u043f\u0440\u0438\u0451\u043c\u0449\u0438\u043a \u043f\u0440\u0438\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u0432 \u0441\u0435\u043a\u0442\u043e\u0440\u0435, \u0433\u0434\u0435 \u043e\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f. \u0422\u0430\u043a \u043e\u043d \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u0442 \u0441\u0438\u0433\u043d\u0430\u043b \u043d\u0430 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0432 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b <strong>stock.py<\/strong>:<\/p>\n<pre><code>@routestock.post(\"\/{stock}\/change\") async def end_load(stock: str, info_worker: shemas.BaseWorker): \"\"\" \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: \\n \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0441\u0435\u043a\u0442\u043e\u0440\u0430 \u0438 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0430 \u043d\u0430<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-368307","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/368307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=368307"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/368307\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=368307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=368307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=368307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}