Skip to content

Commit b269570

Browse files
authored
chore(docs): add README.md (#30)
1 parent 7a3e1a7 commit b269570

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

README.md

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# SmartApp Template Tutorial
2+
3+
## Введение
4+
5+
> :information_source: Инфо
6+
> Для взаимодействия с платформой **botx** используется библиотека **[pybotx](https://github.com/ExpressApp/pybotx)**. В **[документации](https://pypi.org/project/pybotx/)** можно посмотреть примеры её использования.
7+
> Для реализации SmartApp используется библиотека **[pybotx-smartapp-rpc](https://github.com/ExpressApp/pybotx-smartapp-rpc)**.
8+
> Перед прочтением данного туториала следует с ней ознакомиться.
9+
10+
----
11+
12+
Шаблон решает проблему написания повторяющего кода в самом начале работы над проектом.
13+
Уже существует шаблон для разработки ботов **[bot-template](https://github.com/ExpressApp/bot-template)**
14+
15+
Но структура бота и SmartApp отличается друг от друга, поэтому был создан этот шаблон.
16+
17+
----
18+
19+
20+
## 1. Развертывание из шаблона и структура проекта
21+
22+
Для развертывания проекта необходимо установить [copier](https://github.com/copier-org/copier) и выполнить команду:
23+
```bash
24+
$ copier smartapp-template smartapp-example
25+
```
26+
27+
Структура шаблонного SmartApp состоит из нескольких следующих пакетов и модулей:
28+
29+
```
30+
.
31+
├── app
32+
│ ├── api - реализация http роутов для приложения, включая необходимые для smartapp
33+
│ ├── bot - команды бота и вспомогательные функции для них
34+
│ ├── caching - классы и функции для работы с in-memory БД
35+
│ ├── db - модели, функции для работы с БД и миграции
36+
│ ├── schemas - сериализаторы, енамы, доменные модели
37+
│ ├── services - сервисы с логикой (бизнес-логика)
38+
│ ├── smartapp - rpc - методы и аргументы для smartapp
39+
│ ├── smartapp-files - статические файлы
40+
│ ├── constants.py - константы
41+
│ ├── logger.py - логгер
42+
│ ├── main.py - запуск сервера с инициализацией необходимых сервисов
43+
│ └── settings.py - настройки приложения
44+
├── extensions - вспомогательные расширения для шаблона
45+
├── scripts - скрипты для запуска тестов, форматеров, линтеров
46+
├── tests - тесты, структура которых соответствует структуре проекта, и хелперы для них
47+
├── poetry.lock - конфигурация текущих зависимостей. используется для их установки
48+
├── pyproject.toml - конфигурация зависимостей, мета информация проекта (название, версия, авторы и т.п.)
49+
└── setup.cfg - конфигурация линтеров и тестов
50+
```
51+
52+
## 2. Запуск проекта
53+
54+
### Настройка окружения
55+
56+
1. Устанавливаем зависимости проекта через [poetry](https://github.com/python-poetry/poetry#poetry-dependency-management-for-python):
57+
```bash
58+
$ poetry install
59+
```
60+
2. Определяем переменные окружения в файле **`.env`**. Примеры переменных окружения находятся в файле **`example.env`**.
61+
3. Запускаем `postges` и `redis` используя [docker-compose](https://docs.docker.com/compose/):
62+
```bash
63+
$ docker-compose -f docker-compose.dev.yml up -d
64+
```
65+
4. Применяем все миграции для инициализации таблиц с помощью [alembic](https://alembic.sqlalchemy.org/en/latest/tutorial.html):
66+
```bash
67+
$ alembic upgrade head
68+
```
69+
5. Запускаем SmartApp как приложение [FastAPI](https://fastapi.tiangolo.com/tutorial/) через [gunicorn](https://fastapi.tiangolo.com/deployment/server-workers/?h=gunicorn#run-gunicorn-with-uvicorn-workers).
70+
Флаг `--reload` используется только при разработке для автоматического перезапуска сервера при изменениях в коде:
71+
```bash
72+
$ gunicorn "app.main:get_application()" --worker-class uvicorn.workers.UvicornWorker
73+
```
74+
По необходимости добавить флаг `--workers` и их колличество, в данном случае 4 рабочих процесса:
75+
```bash
76+
$ gunicorn "app.main:get_application()" --worker-class uvicorn.workers.UvicornWorker --workers 4
77+
```
78+
79+
----
80+
81+
## 3. Добавление нового функционала
82+
83+
### 3.1. Команды SmartApp
84+
85+
#### Структура пакета команд
86+
Обработчики находятся в пакете **`app.smartapp.rpc_methods`** и группируются в отдельные модули в зависимости от логики.
87+
Обработчики добавляются с помощью **RPCRouter**.
88+
89+
Если в модуле становится слишком много команд, следует разбить его на новые модули и сложить в один пакет с названием старого модуля. Например, так:
90+
91+
```
92+
smartapp
93+
├── rpc_methods
94+
│ ├── common.py
95+
│ ├── jira
96+
│ ├── projects.py
97+
│ ├── issues.py
98+
```
99+
100+
101+
#### Регистрация команд
102+
Для добавления модуля с обработчиками нужно импортировать его из модуля нужного вам модуля в **`app/smartapp/smartapp.py`** и добавить его в `routers` smartapp:
103+
104+
```python3
105+
from pybotx_smartapp_rpc import SmartAppRPC
106+
107+
from app.smartapp.middlewares.smartlogger import smart_logger_middleware
108+
from app.smartapp.rpc_methods import common, jira
109+
110+
smartapp = SmartAppRPC(
111+
routers=[common.rpc, jira.rpc],
112+
middlewares=[smart_logger_middleware],
113+
)
114+
```
115+
116+
----
117+
118+
### 3.2. Взаимодействие с БД
119+
120+
#### Создание новых моделей
121+
122+
Взаимодействовать с новыми таблицами можно через модели [sqlalchemy](https://www.sqlalchemy.org/). С примерами использования можно ознакомиться [тут](https://www.sqlalchemy.org/library.html#tutorials). Модели располагаются в пакете **`app.db.package_name`**. Там же хранятся `crud` функции и [репозитории](https://gist.github.com/maestrow/594fd9aee859c809b043). Структура пакета выглядит следующим образом:
123+
```
124+
├── app
125+
│ ├── db
126+
│ ├── migrations
127+
│ ├── exampleapp
128+
│ ├── repo.py - репозиторий/crud функции
129+
│ ├── models.py - модели таблиц
130+
```
131+
132+
Пример модели:
133+
``` python
134+
from sqlalchemy import Column, Integer, String
135+
from app.db.sqlalchemy import Base
136+
137+
class ExampleModel(Base):
138+
__tablename__ = "examples"
139+
140+
id: int = Column(Integer, primary_key=True, autoincrement=True)
141+
text: str = Column(String)
142+
```
143+
144+
Пример репозитория:
145+
``` python
146+
from sqlalchemy import insert
147+
from app.db.sqlalchemy import session
148+
from app.db.example.models import ExampleModel
149+
150+
class ExampleRepo:
151+
async def create(self, text: str) -> None:
152+
query = insert(ExampleModel).values(text=text)
153+
async with session.begin():
154+
await session.execute(query)
155+
```
156+
157+
#### Создание новых миграций
158+
159+
Для генерации миграций используется [alembic](https://alembic.sqlalchemy.org/en/latest/). Все файлы миграции хранятся в директории **`app.db.migrations`**. Для генерации новой миграции необходимо создать модель `sqlalchemy` и выполнить команду:
160+
161+
```bash
162+
$ alembic revision --autogenerate -m "migration message"
163+
```
164+
165+
Новый файл миграции будет создан в следующей директории:
166+
```
167+
├── app
168+
│ ├── db
169+
│ ├── migrations
170+
│ ├── versions
171+
│ ├── 0123456789ab_migration_message.py
172+
```
173+
174+
Чтобы применить все миграции, следует выполнить команду:
175+
```bash
176+
$ alembic upgrade head
177+
```
178+
или:
179+
```bash
180+
$ alembic upgrade 1
181+
```
182+
для применения только одной миграции.
183+
184+
Для отмены одной миграции необходимо выолнить:
185+
```bash
186+
$ alembic downgrade -1
187+
```
188+
189+
----
190+
191+
### 3.3. Сервисы и бизнес-логика
192+
193+
Вся бизнес-логика проекта выносится в пакет **`app.services`**. Бизнес-логика - логика, характерная только для данного проекта. Туда же выносятся запросы, клиенты для использования API сторонних сервисов, обработка данных по заданным (в ТЗ) правилам.
194+
195+
Структура следующая:
196+
```
197+
├── app
198+
│ ├── services
199+
│ │ ├── errors.py - исключения, вызываемые в клиенте
200+
│ │ ├── client.py - клиент для обращения к стороннему сервису
201+
```
202+
203+
----
204+
205+
### 3.4. Конфиги и переменные среды
206+
207+
Новые переменные среды можно добавить в класс `AppSettings` из файла `app/settings.py`. Если у переменной нет значения по умолчанию, то оно будет браться из файла `.env`.
208+
Чтобы использовать эту переменную в боте, необходимо:
209+
``` python
210+
from app.settings import settings
211+
...
212+
settings.MY_VAR
213+
```
214+
215+
> :information_source: Инфо
216+
> Через переменные среды можно указывать окружения, в которых будет запускаться smartapp. `test`, `dev` или `prod`. Просто добавьте в файл `.env` переменную `APP_ENV=prod`.
217+
218+
----
219+
220+
## 4. Линтеры и форматирование кода
221+
222+
#### Запуск
223+
Для запуска всех форматеров необходимо выполнить скрипт:
224+
```bash
225+
$ ./scripts/format
226+
```
227+
228+
Для запуска всех линтеров необходимо выполнить скрипт:
229+
```bash
230+
$ ./scripts/lint
231+
```
232+
233+
#### Описание
234+
* [black](https://github.com/psf/black)
235+
236+
Используется для форматирования кода к единому стилю: разбивает длинные строки, следит за отступами и импортами.
237+
238+
> :warning: Примечание
239+
> В некоторых моментах isort конфликтует с black. Конфликт решается настройкой файла конфигурации **`setup.cfg`**.
240+
241+
* [isort](https://github.com/timothycrosley/isort)
242+
243+
Используется для сортировки импортов. Сначала импорты из стандартных библиотек python, затем из внешних библиотек и в конце из модулей данного проекта.
244+
Между собой импорты сортируются по алфавиту.
245+
246+
* [autoflake](https://github.com/myint/autoflake)
247+
248+
Используется для удаления неиспользуемых импортов и переменных.
249+
250+
* [mypy](https://github.com/python/mypy)
251+
252+
Используется для проверки типов. Помогает находить некоторые ошибки еще на стадии разработки.
253+
254+
> :warning: Примечание
255+
> К сожалению, не все библиотеки поддерживают типизацию. Чтобы подсказать это **mypy** необходимо добавить следующие строки в файл конфигурации **`setup.cfg`**:
256+
257+
```
258+
[mypy]
259+
260+
# ...
261+
262+
[mypy-your_library_name.*]
263+
ignore_missing_imports = True
264+
```
265+
266+
Некоторые же наоборот имеют специальные плагины для **mypy**, например **pydantic**:
267+
268+
```
269+
[mypy]
270+
plugins = pydantic.mypy
271+
272+
...
273+
274+
[pydantic-mypy]
275+
init_forbid_extra = True
276+
init_typed = True
277+
warn_required_dynamic_aliases = True
278+
warn_untyped_fields = True
279+
```
280+
281+
* [wemake-python-styleguide](https://github.com/wemake-services/wemake-python-styleguide)
282+
283+
Используется для комплексной проверки. Анализирует допустимые имена перменных и их длину, сложность вложенных конструкций, правильную обработку исключений и многое другое. Для каждого типа ошибок есть свой уникальный номер, объяснение, почему так делать не стоит, и объяснение, как делать правильно. Список ошибок можно посмотреть [тут](https://wemake-python-stylegui.de/en/latest/pages/usage/violations/index.html).
284+
285+
> :information_source: Инфо
286+
> В некоторых редких случаях можно игнорировать правила линтера. Для этого необходимо либо прописать комментарий с меткой `noqa` на проблемной строке:
287+
> ```python3
288+
> var = problem_function() # noqa: WPS999
289+
> ```
290+
> либо указать `ignore` ошибки в **`setup.cfg`**:
291+
> ```
292+
> [flake8]
293+
> # ...
294+
> ignore =
295+
> # f-strings are useful
296+
> WPS305,
297+
> ```
298+
> Также можно исключать модули и пакеты.
299+
300+
301+
----
302+
303+
304+
## 5. Тестирование
305+
306+
307+
308+
### 5.1. Запуск и добавление тестов
309+
310+
Все тесты пишутся с помощью библиотеки [pytest](https://docs.pytest.org/en/latest/). Запустить тесты можно командой:
311+
312+
```bash
313+
$ pytest
314+
```
315+
316+
Во время тестирования поднимается docker-контейнер с БД. Порт выбирается свободный, поэтому запущенная локально БД не будет мешать. Если вы хотите запускать тесты используя вашу локальную БД, необходимо добавить в **`.env`** переменную `DB=1`, либо выполнить команду:
317+
```bash
318+
$ DB=1 pytest
319+
```
320+
321+
322+
> :information_source: Инфо
323+
> Поскольку **pytest** не умеет в асинхронные тесты, для работы с ними ему необходим плагин **[pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio)**.
324+
325+
### 5.2. Покрытие
326+
327+
Покрытие показывает процент протестированного исходного кода, как всего, так и отдельных модулей. Покрытие помогает определить какие фрагменты кода не запускались в тестах. Для генерации отчетов покрытия используется плагин [pytest-cov](https://pytest-cov.readthedocs.io/en/latest/reporting.html).
328+
329+
Чтобы не прописывать все флаги каждый раз, можно использовать эти скрипты:
330+
```bash
331+
$ ./scripts/test
332+
$ ./scripts/html-cov-test
333+
```
334+
335+
Первый выводит отчет в терминале, второй генерирует отчет в виде `html`страниц с подсветкой непокрытых участков кода.

0 commit comments

Comments
 (0)