mirror of
https://github.com/EDeev/school_menu.git
synced 2026-10-08 04:59:35 +03:00
Compare commits
5 commits
60c523de1c
...
6bb94afe64
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bb94afe64 | |||
| 59a97485e8 | |||
| 6cccfd202d | |||
| ad20d4adea | |||
| ac866d7df1 |
7 changed files with 605 additions and 625 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
* text=auto eol=lf
|
||||||
|
*.db binary
|
||||||
29
.github/workflows/ci.yml
vendored
Normal file
29
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
- run: pip install ruff==0.14.0 openpyxl==3.1.2 requests==2.31.0
|
||||||
|
- name: Синтаксис и неопределённые имена
|
||||||
|
run: ruff check --select E9,F821,F811 . && python -m py_compile code/*.py
|
||||||
|
- name: Разбор ячеек меню без eval
|
||||||
|
working-directory: code
|
||||||
|
run: |
|
||||||
|
python -c "
|
||||||
|
from pars import arithmetic
|
||||||
|
assert arithmetic('150+30') == 180 and arithmetic('12.5') == 12.5
|
||||||
|
try:
|
||||||
|
arithmetic('__import__(\"os\")')
|
||||||
|
raise SystemExit('eval-подобный ввод не отвергнут')
|
||||||
|
except ValueError:
|
||||||
|
pass"
|
||||||
41
README.en.md
Normal file
41
README.en.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# School Menu Bot
|
||||||
|
|
||||||
|
[Русский](README.md) · **English**
|
||||||
|
|
||||||
|
[](https://github.com/EDeev/school_menu/actions/workflows/ci.yml)
|
||||||
|
|
||||||
|
A school cafeteria Telegram bot: every morning it posts the menu from the school website, takes breakfast,
|
||||||
|
lunch and snack orders by class and compiles a summary for the cafeteria.
|
||||||
|
|
||||||
|
**Status:** school project (2021–2022), completed, archived
|
||||||
|
|
||||||
|
**Stack:** Python 3.10 · aiogram 2 · SQLite · openpyxl (menu from Excel) · pymorphy2
|
||||||
|
|
||||||
|
- Today's and tomorrow's menu (`/today`, `/tomorrow`) and a 06:00 broadcast
|
||||||
|
- Ordering with buttons, cancelling and changing an order
|
||||||
|
- Class registration by code, separate groups for students and the cafeteria
|
||||||
|
- Order summary by class with correct Russian word forms
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
cd code && BOT_TOKEN=token BOT_ID=bot_id TECH_GROUP_ID=group_id python bot.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Portion weights and calories from Excel are parsed without `eval`: only numbers and `+ - * /` are allowed.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
School project (2021–2022). The code is open for study; there is no separate license.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
**Egor Deev** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<sub>⭐ If you find this project useful, give it a star on GitHub!</sub>
|
||||||
|
<p><sub>Made with ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
|
||||||
|
</div>
|
||||||
153
README.md
153
README.md
|
|
@ -1,154 +1,41 @@
|
||||||
# 🍽️ School Menu Bot
|
# School Menu Bot
|
||||||
|
|
||||||
Автоматизированная система управления школьным питанием с интеграцией Telegram Bot API для сбора и обработки заказов.
|
**Русский** · [English](README.en.md)
|
||||||
|
|
||||||
## 🎯 Описание проекта
|
[](https://github.com/EDeev/school_menu/actions/workflows/ci.yml)
|
||||||
|
|
||||||
School Menu Bot представляет собой многофункциональное решение для организации процесса заказа школьного питания. Система обеспечивает автоматический парсинг меню с официального сайта школы, предоставляет интерактивный интерфейс для заказов через Telegram и ведет детальную статистику по группам и пользователям.
|
Telegram-бот школьной столовой: каждое утро присылает меню с сайта школы, принимает заказы завтраков,
|
||||||
|
обедов и полдников по классам и собирает сводку для столовой.
|
||||||
|
|
||||||
## ⭐ Ключевые возможности
|
**Статус:** школьный проект (2021–2022), завершён, в архиве
|
||||||
|
|
||||||
### Пользовательский функционал
|
**Стек:** Python 3.10 · aiogram 2 · SQLite · openpyxl (меню из Excel) · pymorphy2
|
||||||
- **Просмотр меню**: Получение актуального меню на текущий и следующий день
|
|
||||||
- **Интерактивные заказы**: Система выбора питания через inline-клавиатуры
|
|
||||||
- **Гибкое планирование**: Возможность заказа комбинаций (завтрак, обед, полдник)
|
|
||||||
- **Управление заказами**: Отмена и изменение активных заказов
|
|
||||||
|
|
||||||
### Административные функции
|
- Меню на сегодня и завтра (`/today`, `/tomorrow`) и рассылка в 06:00
|
||||||
- **Групповая регистрация**: Система верификации через уникальные коды
|
- Заказ питания кнопками, отмена и изменение заказа
|
||||||
- **Аналитика заказов**: Детальная статистика по группам и пользователям
|
- Регистрация класса по коду, отдельные группы для учеников и для столовой
|
||||||
- **Автоматические уведомления**: Ежедневная рассылка меню в 06:00
|
- Сводка заказов по классам с правильным склонением слов
|
||||||
- **Мультигрупповая поддержка**: Раздельные каналы для учеников и администрации
|
|
||||||
|
|
||||||
### Техническая архитектура
|
## Запуск
|
||||||
- **Парсинг данных**: Автоматическое извлечение меню из Excel-файлов
|
|
||||||
- **Многоуровневая БД**: Система баз данных для пользователей, групп и заказов
|
|
||||||
- **Асинхронная обработка**: Неблокирующее выполнение операций
|
|
||||||
- **Морфологический анализ**: Корректное склонение слов в статистике
|
|
||||||
|
|
||||||
## 🛠️ Технологический стек
|
|
||||||
|
|
||||||
### Backend Framework
|
|
||||||
- **Python 3.8+** - Основной язык разработки
|
|
||||||
- **Aiogram** - Асинхронный фреймворк для Telegram Bot API
|
|
||||||
- **Asyncio** - Асинхронное программирование
|
|
||||||
|
|
||||||
### Data Processing
|
|
||||||
- **OpenPyXL** - Обработка Excel-файлов с меню
|
|
||||||
- **Requests** - HTTP-запросы к внешним ресурсам
|
|
||||||
- **PyMorphy2** - Морфологический анализ русского языка
|
|
||||||
|
|
||||||
### Database Management
|
|
||||||
- **SQLite** - Легковесная встроенная СУБД
|
|
||||||
- **Многоуровневая архитектура БД**:
|
|
||||||
- `base.db` - Основные данные групп и пользователей
|
|
||||||
- `users.db` - Пользовательские связки
|
|
||||||
- `groups.db` - Групповые данные и статистика
|
|
||||||
- `now.db` - Текущие активные заказы
|
|
||||||
|
|
||||||
## 📁 Структура проекта
|
|
||||||
|
|
||||||
```
|
|
||||||
school_menu/
|
|
||||||
├── code/
|
|
||||||
│ ├── bot.py # Основная логика бота
|
|
||||||
│ ├── pars.py # Парсер школьного меню
|
|
||||||
│ ├── sql.py # ORM для работы с БД
|
|
||||||
│ └── base.py # Конфигурация и токены
|
|
||||||
├── db/
|
|
||||||
│ ├── base.db # Основная база данных
|
|
||||||
│ ├── users.db # Пользовательские данные
|
|
||||||
│ ├── groups.db # Групповая статистика
|
|
||||||
│ └── now.db # Текущие заказы (пересоздается ежедневно)
|
|
||||||
├── requirements.txt # Зависимости проекта
|
|
||||||
└── README.md # Документация
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Установка и запуск
|
|
||||||
|
|
||||||
### Предварительные требования
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
cd code && BOT_TOKEN=токен BOT_ID=id_бота TECH_GROUP_ID=id_группы python bot.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Конфигурация
|
Граммовка и калорийность из Excel разбираются без `eval`: допускаются только числа и `+ - * /`.
|
||||||
1. Создайте нового бота через [@BotFather](https://t.me/BotFather)
|
|
||||||
2. Настройте `code/base.py`:
|
|
||||||
```python
|
|
||||||
TOKEN = "ваш_токен_от_BotFather"
|
|
||||||
ID_BOT = ваш_id_бота
|
|
||||||
TEX_GROUP = "-id_технической_группы"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Запуск системы
|
## Лицензия
|
||||||
```bash
|
|
||||||
cd code/
|
|
||||||
python bot.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## 💡 Алгоритм работы
|
Школьный проект (2021–2022). Код открыт для изучения, отдельной лицензии нет.
|
||||||
|
|
||||||
### Процесс регистрации групп
|
## Автор
|
||||||
1. Администратор вызывает `/add_group` в личных сообщениях
|
|
||||||
2. Система генерирует уникальные коды для основной и технической групп
|
|
||||||
3. Коды вводятся в соответствующих чатах для верификации
|
|
||||||
4. Создается связка групп с инициализацией статистики
|
|
||||||
|
|
||||||
### Система заказов
|
**Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
|
||||||
1. Парсинг актуального меню с `school01.ru/food/`
|
|
||||||
2. Пользователь выбирает комбинацию питания через `/eat`
|
|
||||||
3. Заказ фиксируется в основной группе и дублируется в техническую
|
|
||||||
4. Обновление статистики по группам и пользователям
|
|
||||||
|
|
||||||
### Автоматизация процессов
|
|
||||||
- **06:00 ежедневно**: Рассылка меню на следующий день
|
|
||||||
- **Учет выходных**: Автоматический пропуск воскресений
|
|
||||||
- **Сброс заказов**: Ежедневная очистка активных заказов
|
|
||||||
|
|
||||||
## 📊 Команды бота
|
|
||||||
|
|
||||||
| Команда | Описание | Доступность |
|
|
||||||
|---------|----------|-------------|
|
|
||||||
| `/start` | Инициализация бота | Личные сообщения |
|
|
||||||
| `/help` | Справочная информация | Универсально |
|
|
||||||
| `/today` | Меню на сегодня | Универсально |
|
|
||||||
| `/tomorrow` | Меню на завтра | Универсально |
|
|
||||||
| `/eat` | Интерфейс заказа | Только группы |
|
|
||||||
| `/stat` | Статистика группы | Только группы |
|
|
||||||
| `/add_group` | Регистрация групп | Личные сообщения |
|
|
||||||
| `/del_group` | Удаление связки | Личные сообщения |
|
|
||||||
|
|
||||||
## 🎯 Особенности реализации
|
|
||||||
|
|
||||||
### Безопасность данных
|
|
||||||
- Уникальная система кодов для верификации
|
|
||||||
- Раздельное хранение пользовательских и групповых данных
|
|
||||||
- Автоматическая очистка временных данных
|
|
||||||
|
|
||||||
### Производительность
|
|
||||||
- Асинхронная архитектура для обработки множественных запросов
|
|
||||||
- Оптимизированные SQL-запросы с контекстными менеджерами
|
|
||||||
- Кэширование статистических данных
|
|
||||||
|
|
||||||
### Масштабируемость
|
|
||||||
- Модульная архитектура классов для работы с БД
|
|
||||||
- Поддержка неограниченного количества групп
|
|
||||||
- Гибкая система расширения функционала
|
|
||||||
|
|
||||||
## 📋 Лицензия
|
|
||||||
|
|
||||||
Проект распространяется под лицензией MIT.
|
|
||||||
|
|
||||||
## 👥 Автор
|
|
||||||
|
|
||||||
**Деев Егор Викторович** - Backend Developer
|
|
||||||
- GitHub: [@EDeev](https://github.com/EDeev)
|
|
||||||
- Email: egor@deev.space
|
|
||||||
- Telegram: [@Egor_Deev](https://t.me/Egor_Deev)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<sub>Разработано для автоматизации процессов школьного питания с упором на удобство использования и надежность системы.</sub>
|
<sub>⭐ Если проект оказался полезным, поставьте звёздочку на GitHub!</sub>
|
||||||
<p><sub>Создано с ❤️ от вашего дорогого - deev.space ©</sub></p>
|
<p><sub>Сделано с ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
10
code/base.py
10
code/base.py
|
|
@ -1,4 +1,8 @@
|
||||||
TOKEN = "token from BotFather"
|
import os
|
||||||
|
|
||||||
ID_BOT = **********
|
# Токен бота от @BotFather — из переменной окружения BOT_TOKEN
|
||||||
TEX_GROUP = "-*********"
|
TOKEN = os.environ.get("BOT_TOKEN", "")
|
||||||
|
|
||||||
|
# Числовой ID бота и ID технической группы для служебных сообщений — тоже из окружения
|
||||||
|
ID_BOT = int(os.environ.get("BOT_ID", "0"))
|
||||||
|
TEX_GROUP = os.environ.get("TECH_GROUP_ID", "")
|
||||||
|
|
|
||||||
21
code/pars.py
21
code/pars.py
|
|
@ -1,7 +1,24 @@
|
||||||
|
import ast
|
||||||
|
import operator
|
||||||
import requests as req
|
import requests as req
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import openpyxl, os, re
|
import openpyxl, os, re
|
||||||
|
|
||||||
|
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv}
|
||||||
|
|
||||||
|
|
||||||
|
def arithmetic(text):
|
||||||
|
"""Значение ячейки вида «150» или «150+30»: только числа и + - * /, без eval."""
|
||||||
|
def calc(node):
|
||||||
|
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
|
||||||
|
return node.value
|
||||||
|
if isinstance(node, ast.BinOp) and type(node.op) in _OPS:
|
||||||
|
return _OPS[type(node.op)](calc(node.left), calc(node.right))
|
||||||
|
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
|
||||||
|
return -calc(node.operand)
|
||||||
|
raise ValueError(f"недопустимое значение в ячейке: {text!r}")
|
||||||
|
return calc(ast.parse(text, mode="eval").body)
|
||||||
|
|
||||||
|
|
||||||
def eating(day):
|
def eating(day):
|
||||||
if day == 0:
|
if day == 0:
|
||||||
|
|
@ -38,8 +55,8 @@ def eating(day):
|
||||||
if employees_sheet[f"D{j[1] + i}"].value is None:
|
if employees_sheet[f"D{j[1] + i}"].value is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
gramm = str(int(eval(str(employees_sheet[f"E{j[1] + i}"].value).strip())))
|
gramm = str(int(arithmetic(str(employees_sheet[f"E{j[1] + i}"].value).strip())))
|
||||||
kkal = str(eval("".join(str(employees_sheet[f"J{j[1] + i}"].value).split()).replace(",", ".", 1)))
|
kkal = str(arithmetic("".join(str(employees_sheet[f"J{j[1] + i}"].value).split()).replace(",", ".", 1)))
|
||||||
|
|
||||||
food[j[0]].append([employees_sheet[f"D{j[1] + i}"].value, [gramm, kkal]])
|
food[j[0]].append([employees_sheet[f"D{j[1] + i}"].value, [gramm, kkal]])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue