From 69132570aa6eea706684d3bf82dba53f25e42a8d Mon Sep 17 00:00:00 2001 From: zhouchengrong Date: Wed, 20 Mar 2024 11:44:15 +0800 Subject: [PATCH] 1 --- .idea/.gitignore | 8 + .idea/inspectionProfiles/Project_Default.xml | 324 ++++++++++++++++++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/trinity_client_aida.iml | 8 + .idea/vcs.xml | 6 + Dockerfile | 22 ++ README.md | 45 +++ app/__init__.py | 0 app/api/__init__.py | 0 app/api/api_route.py | 9 + app/api/api_super_resolution.py | 14 + app/api/api_test.py | 14 + app/core/__init__.py | 0 app/core/config.py | 53 +++ app/logs/debug.log | 1 + app/logs/errors.log | 28 ++ app/logs/info.log | 1 + app/main.py | 38 ++ app/schemas/super_resolution.py | 6 + app/service/super_resolution/service.py | 67 ++++ app/service/super_resolution/test.py | 28 ++ app/service/utils/decorator.py | 14 + app/service/utils/generate_uuid.py | 10 + docker-compose.yml | 9 + gunicorn_config.py | 38 ++ logging_env.py | 51 +++ requirements.txt | Bin 0 -> 848 bytes 29 files changed, 815 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/trinity_client_aida.iml create mode 100644 .idea/vcs.xml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 app/api/__init__.py create mode 100644 app/api/api_route.py create mode 100644 app/api/api_super_resolution.py create mode 100644 app/api/api_test.py create mode 100644 app/core/__init__.py create mode 100644 app/core/config.py create mode 100644 app/logs/debug.log create mode 100644 app/logs/errors.log create mode 100644 app/logs/info.log create mode 100644 app/main.py create mode 100644 app/schemas/super_resolution.py create mode 100644 app/service/super_resolution/service.py create mode 100644 app/service/super_resolution/test.py create mode 100644 app/service/utils/decorator.py create mode 100644 app/service/utils/generate_uuid.py create mode 100644 docker-compose.yml create mode 100644 gunicorn_config.py create mode 100644 logging_env.py create mode 100644 requirements.txt diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..1ccf847 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,324 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f2c1475 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..acab9f4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/trinity_client_aida.iml b/.idea/trinity_client_aida.iml new file mode 100644 index 0000000..6868300 --- /dev/null +++ b/.idea/trinity_client_aida.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1795331 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.9 +ENV TZ=Asia/Shanghai +RUN apt update +RUN apt install -y vim +RUN apt install -y libgl1-mesa-glx +COPY ./requirements.txt /requirements.txt +RUN pip install --upgrade pip +RUN pip install -r requirements.txt +RUN pip install gunicorn +RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 +RUN pip install mmcv==1.4.2 -f https://download.openmmlab.com/mmcv/dist/cu117/torch1.13/index.html + +WORKDIR /app +COPY . . +ENV FLASK_APP=manage.py +LABEL maintainer="zchengrong@yeah.net" \ + description="My Python 3.9 - trinity aida " \ + version="1.0" \ + name="trinity_aida" + + +CMD ["gunicorn", "-c", "gunicorn_config.py", "app.main:app"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f028e6b --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +文件解释 +----------- + +样例包括: + +* README.md - 本文件 +* Dockerfile - 用以自动构建 Docker 镜像的脚本 +* requirements.txt - 依赖包文件 +* main.py - 主 Flask 服务器端源代码 +* python-version : 3.9 + +快速开始 +--------------- + +如下这些引导,假定你想在自己的电脑上开发本项目。 + +1. 安装依赖 + + $ conda create -n trinity_client_aida python=3.9 -y + $ conda activate trinity_client_aida + $ pip install -r requirements.txt + $ conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia -y + $ pip install mmcv==1.4.2 -f https://download.openmmlab.com/mmcv/dist/cu117/torch1.13/index.html + + +2. 启动服务器 + + $ uvicorn app.main:app --host 0.0.0.0 --port 8000 + +3. 打开 http://127.0.0.1:8000/docs + +Docker 部署 +--------------- +1. 构建镜像 + + $ cd {workspace} + $ docker build -t trinity_client_mixi + +2. 使用docker-compose 启动 + + $ docker-compose up -d + +3. 查看日志 + + $ docker-compose logs -f \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/api_route.py b/app/api/api_route.py new file mode 100644 index 0000000..fd299ee --- /dev/null +++ b/app/api/api_route.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter + +from app.api import api_test +from app.api import api_super_resolution + +router = APIRouter() + +router.include_router(api_test.router, tags=["test"], prefix="/test") +router.include_router(api_super_resolution.router, tags=["api_super_resolution"], prefix="/api") diff --git a/app/api/api_super_resolution.py b/app/api/api_super_resolution.py new file mode 100644 index 0000000..aa9d82f --- /dev/null +++ b/app/api/api_super_resolution.py @@ -0,0 +1,14 @@ +from fastapi import APIRouter + +from app.schemas.super_resolution import SuperResolutionModel +from app.service.super_resolution.service import SuperResolution + +router = APIRouter() + + +@router.post("super_resolution") +def super_resolution(request_item: SuperResolutionModel): + service = SuperResolution() + sr_result_url = service.sr_result(request_item.sr_image_url, request_item.sr_xn) + response = {"sr_result_url": sr_result_url} + return {"code": 200, "message": "ok", "data": response} diff --git a/app/api/api_test.py b/app/api/api_test.py new file mode 100644 index 0000000..cd9df51 --- /dev/null +++ b/app/api/api_test.py @@ -0,0 +1,14 @@ +import logging + +from fastapi import APIRouter + +logger = logging.getLogger() +router = APIRouter() + + + + +@router.get("") +def test(): + logger.info("test") + return {"message": "ok"} diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..ec021bb --- /dev/null +++ b/app/core/config.py @@ -0,0 +1,53 @@ +import os +from dotenv import load_dotenv +from pydantic import BaseSettings + +BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')) +load_dotenv(os.path.join(BASE_DIR, '.env')) + + +class Settings(BaseSettings): + PROJECT_NAME = os.getenv('PROJECT_NAME', 'FASTAPI BASE') + SECRET_KEY = os.getenv('SECRET_KEY', '') + API_PREFIX = '' + BACKEND_CORS_ORIGINS = ['*'] + DATABASE_URL = os.getenv('SQL_DATABASE_URL', '') + ACCESS_TOKEN_EXPIRE_SECONDS: int = 60 * 60 * 24 * 7 # Token expired after 7 days + SECURITY_ALGORITHM = 'HS256' + LOGGING_CONFIG_FILE = os.path.join(BASE_DIR, 'logging_env.py') + + +settings = Settings() + +ckpt = 'service/super_resolution_ccsr/weights/real-world_ccsr.ckpt' +config = 'service/super_resolution_ccsr/configs/model/ccsr_stage2.yaml' +steps = 45 +sr_scale = 4 +repeat_times = 1 +tiled = False +tile_size = 512 +tile_stride = 256 +color_fix_type = "adain" +t_max = 0.6667 +t_min = 0.3333 +show_lq = False +skip_if_exist = False +seed = 233 +device = "cuda" +tile_diffusion = False # +tile_diffusion_size = 512 +tile_diffusion_stride = 256 +tile_vae = True +vae_decoder_tile_size = 224 +vae_encoder_tile_size = 1024 +strength = 1 +# minio 配置 +sr_bucket = "test" +MINIO_IP = "www.minio.aida.com.hk" +MINIO_PORT = 9000 +MINIO_ACCESS = 'vXKFLSJkYeEq2DrSZvkB' +MINIO_SECRET = 'uKTZT3x7C43WvPN9QTc99DiRkwddWZrG9Uh3JVlR' +MINIO_SECURE = True +# input = 'preprocess_img/input_x2' # 这个值需要被函数参数覆盖 +# output = '/path/to/output' # 这个值将被函数参数覆盖 +LOGS_PATH = "logs/errors.log" diff --git a/app/logs/debug.log b/app/logs/debug.log new file mode 100644 index 0000000..3ff8d34 --- /dev/null +++ b/app/logs/debug.log @@ -0,0 +1 @@ +2024-03-12 13:03:10,034 main.py [line:43] INFO test ok diff --git a/app/logs/errors.log b/app/logs/errors.log new file mode 100644 index 0000000..014b163 --- /dev/null +++ b/app/logs/errors.log @@ -0,0 +1,28 @@ +2024-03-20 11:41:28,641 decorator.py [line:11] INFO function:【read_image】,runtime:【2.3682610988616943】s +2024-03-20 11:41:28,641 decorator.py [line:11] INFO function:【read_image】,runtime:【2.3682610988616943】s +2024-03-20 11:41:28,978 decorator.py [line:11] INFO function:【sr_result】,runtime:【2.7045106887817383】s +2024-03-20 11:41:28,978 decorator.py [line:11] INFO function:【sr_result】,runtime:【2.7045106887817383】s +2024-03-20 11:41:40,123 decorator.py [line:11] INFO function:【read_image】,runtime:【6.277707099914551】s +2024-03-20 11:41:40,123 decorator.py [line:11] INFO function:【read_image】,runtime:【6.277707099914551】s +2024-03-20 11:41:40,439 decorator.py [line:11] INFO function:【sr_result】,runtime:【6.594382047653198】s +2024-03-20 11:41:40,439 decorator.py [line:11] INFO function:【sr_result】,runtime:【6.594382047653198】s +2024-03-20 11:41:41,338 decorator.py [line:11] INFO function:【read_image】,runtime:【0.16055655479431152】s +2024-03-20 11:41:41,338 decorator.py [line:11] INFO function:【read_image】,runtime:【0.16055655479431152】s +2024-03-20 11:41:41,643 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.46419310569763184】s +2024-03-20 11:41:41,643 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.46419310569763184】s +2024-03-20 11:41:42,625 decorator.py [line:11] INFO function:【read_image】,runtime:【0.15813016891479492】s +2024-03-20 11:41:42,625 decorator.py [line:11] INFO function:【read_image】,runtime:【0.15813016891479492】s +2024-03-20 11:41:42,929 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.4632871150970459】s +2024-03-20 11:41:42,929 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.4632871150970459】s +2024-03-20 11:41:48,216 decorator.py [line:11] INFO function:【read_image】,runtime:【0.1381824016571045】s +2024-03-20 11:41:48,216 decorator.py [line:11] INFO function:【read_image】,runtime:【0.1381824016571045】s +2024-03-20 11:41:48,537 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.4588344097137451】s +2024-03-20 11:41:48,537 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.4588344097137451】s +2024-03-20 11:42:48,128 decorator.py [line:11] INFO function:【read_image】,runtime:【0.15878772735595703】s +2024-03-20 11:42:48,128 decorator.py [line:11] INFO function:【read_image】,runtime:【0.15878772735595703】s +2024-03-20 11:42:48,463 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.49385905265808105】s +2024-03-20 11:42:48,463 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.49385905265808105】s +2024-03-20 11:43:24,220 decorator.py [line:11] INFO function:【read_image】,runtime:【0.16216182708740234】s +2024-03-20 11:43:24,220 decorator.py [line:11] INFO function:【read_image】,runtime:【0.16216182708740234】s +2024-03-20 11:43:24,563 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.5048878192901611】s +2024-03-20 11:43:24,563 decorator.py [line:11] INFO function:【sr_result】,runtime:【0.5048878192901611】s diff --git a/app/logs/info.log b/app/logs/info.log new file mode 100644 index 0000000..3ff8d34 --- /dev/null +++ b/app/logs/info.log @@ -0,0 +1 @@ +2024-03-12 13:03:10,034 main.py [line:43] INFO test ok diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..ad48d7d --- /dev/null +++ b/app/main.py @@ -0,0 +1,38 @@ +import logging.config + +import uvicorn +from fastapi import FastAPI + +from app.api.api_route import router +from app.core.config import settings +from logging_env import LOGGER_CONFIG_DICT + +logging.config.dictConfig(LOGGER_CONFIG_DICT) + +from starlette.middleware.cors import CORSMiddleware + + +def get_application() -> FastAPI: + application = FastAPI( + title=settings.PROJECT_NAME, docs_url="/docs", redoc_url='/re-docs', + openapi_url=f"{settings.API_PREFIX}/openapi.json", + description=''' + Base frame with FastAPI + - Super Resolution API + + ''' + ) + application.add_middleware( + CORSMiddleware, + allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + application.include_router(router=router, prefix=settings.API_PREFIX) + return application + + +app = get_application() +if __name__ == '__main__': + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/app/schemas/super_resolution.py b/app/schemas/super_resolution.py new file mode 100644 index 0000000..1b9bdb7 --- /dev/null +++ b/app/schemas/super_resolution.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +class SuperResolutionModel(BaseModel): + sr_image_url: str + sr_xn: int diff --git a/app/service/super_resolution/service.py b/app/service/super_resolution/service.py new file mode 100644 index 0000000..5dd323a --- /dev/null +++ b/app/service/super_resolution/service.py @@ -0,0 +1,67 @@ +import io +import logging +from io import BytesIO + +import cv2 +import numpy as np +import torch +import tritonclient.http as httpclient +from minio import Minio + +from app.core.config import MINIO_IP, MINIO_ACCESS, MINIO_SECRET, MINIO_SECURE, MINIO_PORT + +from app.service.utils.decorator import RunTime +from app.service.utils.generate_uuid import generate_uuid + +logger = logging.getLogger() + + +class SuperResolution: + def __init__(self): + self.triton_client = httpclient.InferenceServerClient(url=f"10.1.1.150:7000") + self.minio_client = Minio( + f"{MINIO_IP}:{MINIO_PORT}", + access_key=MINIO_ACCESS, + secret_key=MINIO_SECRET, + secure=MINIO_SECURE) + + @RunTime + def read_image(self, image_url): + image_data = self.minio_client.get_object(image_url.split("/", 1)[0], image_url.split("/", 1)[1]) + img = np.frombuffer(image_data.data, np.uint8) # 转成8位无符号整型 + img = cv2.imdecode(img, cv2.IMREAD_COLOR).astype(np.float32) / 255. # 解码 + return img + + @RunTime + def sr_result(self, image_url, sr_xn): + sample = self.read_image(image_url) + sample = np.transpose(sample if sample.shape[2] == 1 else sample[:, :, [2, 1, 0]], (2, 0, 1)) + sample = torch.from_numpy(sample).float().unsqueeze(0).numpy() + inputs = [ + httpclient.InferInput("input", sample.shape, datatype="FP32") + ] + inputs[0].set_data_from_numpy(sample, binary_data=True) + results = self.triton_client.infer(model_name="super_resolution", inputs=inputs) + + sr_output = torch.from_numpy(results.as_numpy(f"output")) + output = sr_output.data.squeeze().float().cpu().clamp_(0, 1).numpy() + if output.ndim == 3: + output = np.transpose(output[[2, 1, 0], :, :], (1, 2, 0)) # CHW-RGB to HCW-BGR + output = (output * 255.0).round().astype(np.uint8) + output_url = self.upload_img_sr(output, generate_uuid()) + return output_url + + def upload_img_sr(self, image, object_name): + try: + image_bytes = cv2.imencode('.jpg', image)[1].tobytes() + image_url = f"test/{self.minio_client.put_object(f'test', f'{object_name}.jpg', io.BytesIO(image_bytes), len(image_bytes), content_type='image/png').object_name}" + + return image_url + except Exception as e: + logger.warning(f"upload_png_mask runtime exception : {e}") + + +if __name__ == '__main__': + service = SuperResolution() + result_url = service.sr_result("test/128_image/11.png", 4) + print(result_url) diff --git a/app/service/super_resolution/test.py b/app/service/super_resolution/test.py new file mode 100644 index 0000000..bc35925 --- /dev/null +++ b/app/service/super_resolution/test.py @@ -0,0 +1,28 @@ +import time + +import cv2 +import numpy as np +import torch +import tritonclient.http as httpclient +from PIL import Image + +triton_client = httpclient.InferenceServerClient(url=f"10.1.1.150:7000") + +sample = cv2.imread("comic2.png", cv2.IMREAD_COLOR).astype(np.float32) / 255. +sample = np.transpose(sample if sample.shape[2] == 1 else sample[:, :, [2, 1, 0]], (2, 0, 1)) +sample = torch.from_numpy(sample).float().unsqueeze(0).numpy() +inputs = [ + httpclient.InferInput("input", sample.shape, datatype="FP32") +] +inputs[0].set_data_from_numpy(sample, binary_data=True) +start_time = time.time() +results = triton_client.infer(model_name="super_resolution", inputs=inputs) +print(time.time() - start_time) +sr_output = torch.from_numpy(results.as_numpy(f"output")) +output = sr_output.data.squeeze().float().cpu().clamp_(0, 1).numpy() +if output.ndim == 3: + output = np.transpose(output[[2, 1, 0], :, :], (1, 2, 0)) # CHW-RGB to HCW-BGR +output = (output * 255.0).round().astype(np.uint8) +# cv2.imshow("", output) +# cv2.waitKey(0) +cv2.imwrite("comic3.png", output) diff --git a/app/service/utils/decorator.py b/app/service/utils/decorator.py new file mode 100644 index 0000000..294b54b --- /dev/null +++ b/app/service/utils/decorator.py @@ -0,0 +1,14 @@ +import time +import logging + + +def RunTime(func): + def wrapper(*args, **kwargs): + t1 = time.time() + res = func(*args, **kwargs) + t2 = time.time() + if t2 - t1 > 0.05: + logging.info(f"function:【{func.__name__}】,runtime:【{str(t2 - t1)}】s") + return res + + return wrapper diff --git a/app/service/utils/generate_uuid.py b/app/service/utils/generate_uuid.py new file mode 100644 index 0000000..30f9ed8 --- /dev/null +++ b/app/service/utils/generate_uuid.py @@ -0,0 +1,10 @@ +import threading +import uuid + +id_lock = threading.Lock() + + +def generate_uuid(): + with id_lock: + unique_id = str(uuid.uuid1()) + return unique_id diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b3dfd4d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3" +services: + trinity_mixi: + image: "trinity_client_mixi:latest" + container_name: trinity_mixi + volumes: + - ./trinity_client_mixi:/trinity + ports: + - "10100:4562" \ No newline at end of file diff --git a/gunicorn_config.py b/gunicorn_config.py new file mode 100644 index 0000000..c85e8ad --- /dev/null +++ b/gunicorn_config.py @@ -0,0 +1,38 @@ +# 多进程 + +"""gunicorn+gevent 的配置文件""" + +# 预加载资源 +preload_app = True +# 绑定 ip + 端口 +bind = "0.0.0.0:4562" +# 进程数 = cup数量 * 2 + 1 +workers = 1 + +# 线程数 = cup数量 * 2 +threads = 2 + +# 等待队列最大长度,超过这个长度的链接将被拒绝连接 +backlog = 2048 + +# 工作模式--协程 +worker_class = "uvicorn.workers.UvicornWorker" + +# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响 +# 服务器配置设置的值 1200:中小型项目 上万并发: 中大型 +# 服务器硬件:宽带+数据库+内存 +# 服务器的架构:集群 主从 +worker_connections = 1200 + +# 进程名称 +proc_name = 'gunicorn.pid' +# 进程pid记录文件 +pidfile = 'app_run.log' +# 日志等级 +loglevel = 'info' +# 日志文件名 +logfile = 'info.log' +# 访问记录 +accesslog = 'access.log' +# 访问记录格式 +access_log_format = '%(h)s %(t)s %(U)s %(q)s' diff --git a/logging_env.py b/logging_env.py new file mode 100644 index 0000000..ce4e9da --- /dev/null +++ b/logging_env.py @@ -0,0 +1,51 @@ +from app.core.config import LOGS_PATH + +LOGGER_CONFIG_DICT = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "simple": {"format": "%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s"} + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "level": "DEBUG", + "formatter": "simple", + "stream": "ext://sys.stdout", + }, + "info_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "level": "INFO", + "formatter": "simple", + "filename": LOGS_PATH, + "maxBytes": 10485760, + "backupCount": 50, + "encoding": "utf8", + }, + "error_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "level": "ERROR", + "formatter": "simple", + "filename": LOGS_PATH, + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8", + }, + "debug_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "level": "DEBUG", + "formatter": "simple", + "filename": LOGS_PATH, + "maxBytes": 10485760, + "backupCount": 50, + "encoding": "utf8", + }, + }, + "loggers": { + "my_module": {"level": "INFO", "handlers": ["console"], "propagate": "no"} + }, + "root": { + "level": "INFO", + "handlers": ["error_file_handler", "info_file_handler", "debug_file_handler", "console"], + }, +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..99ab7d327ec5e353fd95960ebf813d4db3424397 GIT binary patch literal 848 zcmZXT-A;p05QO*I#CIU^8dK_p@xo^@2oyl62l-P;|=3C4%*ZM58K?!g+Kznxz=PdVa#5$&vkbAYkvZCy30 zHQ*!oMBN2{zN)3{ii&U6!)|#Bh)NmpRjE=)b07gHqI*QC0nu`x?LlZ<;}HXYU^GiHQ|+THHuSJ*uWQP!g-%X>>2f z2>-TsoumrpG0|83&OSkut+(3q1JUZdNu4&GpLl`>Fzq^ec(4qnX0FW4v3Op!(abg1 nxn;?;(|x7!lI_PHud}r^VI|HApMOCGMX#AlRNp}7YO%wAe+!Mp literal 0 HcmV?d00001