Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:Studio-Yandex-Practicum-Hackatho…
Browse files Browse the repository at this point in the history
…ns/cafe_azu_bot_2 into develop
  • Loading branch information
Certelen committed Nov 28, 2023
2 parents 713b7a7 + 6f2b117 commit 5557291
Show file tree
Hide file tree
Showing 33 changed files with 627 additions and 267 deletions.
63 changes: 63 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Django-app workflow

on: [push]

jobs:
tests:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.11

- name: Install dependencies
run: |
# обновление pip
python -m pip install --upgrade pip
pip install flake8 pep8-naming flake8-broken-line flake8-return flake8-isort
pip install -r requirements.txt
- name: Test with flake8 and django tests
run: |
python -m flake8
build_and_push_to_docker_hub:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
needs: tests
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push to Docker Hub
uses: docker/build-push-action@v2
with:
push: true
tags: certelen/azu_cafe_django

deploy:
runs-on: ubuntu-latest
needs: build_and_push_to_docker_hub
steps:
- name: executing remote ssh commands to deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
key: ${{ secrets.SSH_KEY }}
passphrase: ${{ secrets.PASSPHRASE }}
script: |
# Выполняет pull образа с DockerHub
sudo docker pull certelen/azu_cafe_django
#остановка всех контейнеров
sudo docker stop $(sudo docker ps -a -q)
sudo docker run --rm -d -p 5000:5000 certelen/azu_cafe_django
128 changes: 66 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,103 +4,107 @@

## Технологии
- Python 3.11
- aiogram 3.1.1
- Django 4.2.7
- Django REST Framework 3.14.0
- Aiogram 3.1.1
- PostgreSQL 13.0
- Nginx 1.21.3
- Docker
- Docker-compose
- Docker Hub
- SunriseSunset.io API

# Установка
## Копирование репозитория
Клонируем репозиторий
Клонируем репозиторий и переходим в директорию infra:
```
~ git clone [email protected]:Studio-Yandex-Practicum-Hackathons/cafe_azu_bot_2.git
~ cd ./cafe_azu_bot_2/infra/
```
Требуется изменить server_name и listen в ./infra/nginx/default.conf, ports в docker-compose.yml

## Виртуальное окружение
Переходим в клонированный репозиторий
## Подготовка боевого сервера:
1. Перейдите на боевой сервер:
```
~ cd {путь до папки с клонированным репозиторем}
~ cd cafe_azu_bot_2
ssh username@server_address
```
Устанавливаем и активируем виртуальное окружение
2. Обновите индекс пакетов APT:
```
~ py -3.11 -m venv venv
~ . venv/Scripts/activate
sudo apt update
```
Устанавливаем требуемые зависимости:
и обновите установленные в системе пакеты и установите обновления безопасности:
```
~ pip install -r requirements.txt
sudo apt upgrade -y
```

## База данных
В azu_bot_django/azu_bot_django/settings.py выбрать нужную базу данных (sqlite3 или postgresql)
### env
Выполнять только если ваша база данных postgresql, иначе пропустить этот шаг.

Создать файл .env со следующими строками для базы данных:
- DATABASE_NAME = <Имя для подключения к базе данных>
- DATABASE_USERNAME = <Имя пользователя для подключения к базе данных>
- DATABASE_PASSWORD = <Пароль для подключения к базе данных>
- DATABASE_HOST = <Адрес для подключения к базе данных>
- DATABASE_PORT = <Порт для подключения к базе данных>

### Миграция базы данных
Переходим в раздел с базой данных:
Создайте папку `nginx`:
```
~ cd azu_bot_django
mkdir nginx
```
Скопируйте файлы docker-compose.yaml, nginx/default.conf из вашего проекта на сервер в home/<ваш_username>/docker-compose.yaml, home/<ваш_username>/nginx/default.conf соответственно:
```
Создаем миграцию для базы данных:
scp docker-compose.yaml <username>@<host>/home/<username>/docker-compose.yaml
scp default.conf <username>@<host>/home/<username>/nginx/default.conf
```
~ py manage.py makemigrations cafe tables menu admin_users reservation
Установите Docker и Docker-compose:
```
И применяем её:
sudo apt install docker.io
```
~ py manage.py migrate
```
Создаем суперпользователя:
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
```
~ py manage.py createsuperuser
```
Вводим поочередно Имя пользователя, почту и пароль.
После чего запускаем базу данных:
sudo chmod +x /usr/local/bin/docker-compose
```
~ uvicorn azu_bot_django.asgi:application
Проверьте правильность установки Docker-compose:
```

### Наполнение базы данных
Доступ к сервису становится доступен по [адресу](http://127.0.0.1:8000/admin/)

По указаному адресу нужно создать хотя бы по одному экземпляру кафе, стола, блюда, сета.

Так же можно наполнить базу данных готовыми сетами, блюдами, кафе и столам:

sudo docker-compose --version
```
~ py manage.py load_data
На боевом сервере создайте файл .env:
```

Создать пользователя-бота и по [адресу](http://127.0.0.1:8000/admin/authtoken/tokenproxy/) нужно создать токен для бота.

## Телеграмм-бот
### env
В уже существующем файле .env добавить строки:
touch .env
```
и заполните переменные окружения
```
nano .env
- TOKEN = <Токен бота, можно получить у BotFather>
- ADMIN_ID = <ID телеграмм-аккаунта админа>
- PROVIDER_TOKEN = <Токен платежной системы>
- DJANGO_TOKEN = <Токен для бота, который был создан в разделе базы данных>
- SECRET_KEY=<SECRET_KEY>
### Запуск
Для полного запуска телеграмм-бота с базой данных нужно:
- Выполнить все вышеуказанные действия по установке.
- Перейти в папку с базой данных и запустить её:
- POSTGRES_DB = postgres
- POSTGRES_USER = postgres
- POSTGRES_PASSWORD = postgres
- POSTGRES_HOST = db
- POSTGRES_PORT = 5432
- WEB_HOST = <ip сервера>
- WEB_PORT = <Порт сервера>
- WEB_PROTOKOL = <Протокол сервера>
```

## Развертывание проекта с помощью Docker:
Разворачиваем контейнеры в фоновом режиме из папки infra:
```
sudo docker compose up -d
```
При первом запуске выполняем миграции:
```
~ cd azu_bot_django
~ uvicorn azu_bot_django.asgi:application
sudo docker compose exec backend python manage.py migrate
```
- Выйти из папки с базой данных, перейти в папку бота, запустить бота:
И собираем статику:
```
~ cd azu_bot_aiogram
~ py main.py
sudo docker compose exec backend python manage.py collectstatic --no-input
```
Телеграмм-бот готов к работе.
Создаем суперпользователя:
```
sudo docker compose exec backend python manage.py createsuperuser
```
Загружаем данные из csv-таблиц в базу данных:
```
sudo docker compose exec backend python manage.py load_data
```

# Адресные пути
- [Документация к API базе данных](http://127.0.0.1:8000/redoc)
- [Админ-панель базы данных](http://127.0.0.1:8000/admin)
Expand Down
9 changes: 9 additions & 0 deletions azu_bot_aiogram/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3.11-slim

WORKDIR /app

COPY ./ ./

RUN pip3 install -r requirements.txt --no-cache-dir

CMD ["python", "main.py"]
25 changes: 21 additions & 4 deletions azu_bot_aiogram/filters/is_correct_person_amount.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import re

from aiogram import Bot
from aiogram.filters import BaseFilter
from aiogram.fsm.context import FSMContext
from aiogram.types import Message
from handlers.api import get_cafe, post_quantity


class IsPersonAmount(BaseFilter):
async def __call__(self, message: Message) -> bool:
"""Проверка корректности ввода количества человек."""
pattern = r'[1-4]$'
pattern = r'^(0?[1-9]|[123][0-9]|4[0])$'
if re.match(pattern, message.text):
return {'amount': message.text}
else:
return False


class TooManyPersons(BaseFilter):
async def __call__(self, message: Message) -> bool:
async def __call__(
self, message: Message, bot: Bot, state: FSMContext
) -> bool:
"""Проверка числа клиентов для брони столов, если клиентов много."""
max_table_size = 4
if message.text.isdigit() and int(message.text) > max_table_size:
cafes = await get_cafe(bot)
context_data = await state.get_data()
address_cafe = context_data.get('address')
for cafe in cafes:
if cafe['address'] == address_cafe:
break
data_dict = {}
data_dict['date'] = '-'.join(
context_data.get('date').split('.')[::-1]
)
data_dict['quantity'] = 0
check_current_cafe = await post_quantity(cafe['id'], data=data_dict)
free_places = check_current_cafe['quantity']
if message.text.isdigit() and int(message.text) > int(free_places):
return {'amount': message.text}
else:
return False
38 changes: 14 additions & 24 deletions azu_bot_aiogram/handlers/api.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
import aiohttp
from settings import django_token, settings


async def get_session():
session = aiohttp.ClientSession(
headers={"Authorization": f'Token {django_token}'}
)
return session
async def get_cafe(bot=None):
async with aiohttp.ClientSession() as session:
async with session.get(
'http://backend:8000/cafes/'
) as response:
return await response.json()


async def get_cafe(bot=None):
mysession = await get_session()
async with mysession as session:
async def get_cafe_admins(cafe):
async with aiohttp.ClientSession() as session:
async with session.get(
'http://127.0.0.1:8000/cafes/'
f'http://backend:8000/cafes/{cafe}/admins/'
) as response:
response = await response.json()
if not isinstance(response, list):
await bot.send_message(
chat_id=settings.bots.admin_id,
text='Нет связи с базой данных!'
'Проверьте валидность django token!')
return None
return response
return await response.json()


async def post_quantity(cafe, data):
mysession = await get_session()
async with mysession as session:
async with aiohttp.ClientSession() as session:
async with session.post(
f'http://127.0.0.1:8000/cafes/{cafe}/quantity/', json=data
f'http://backend:8000/cafes/{cafe}/quantity/', json=data
) as response:
return await response.json()


async def post_reservation(cafe_id, data):
mysession = await get_session()
async with mysession as session:
async with aiohttp.ClientSession() as session:
async with session.post(
f'http://127.0.0.1:8000/cafes/{cafe_id}/reservations/', json=data
f'http://backend:8000/cafes/{cafe_id}/reservations/', json=data
) as response:
return await response.json()
Loading

0 comments on commit 5557291

Please sign in to comment.