mirror of
https://github.com/EDeev/tablo.git
synced 2026-10-07 20:49:31 +03:00
README на русском и английском, документация, скриншоты
- README.md и README.en.md: что это, статус учебного проекта, скриншоты, быстрый старт в Docker, установка без Docker, переменные, схема, развёртывание, тесты; лишний Bootstrap из стека убран (стили свои); - docs/architecture.md (структура, формат расписания, трекеры, права) и docs/deploy.md; - пояснительная записка перенесена в docs/.
This commit is contained in:
parent
999ca04f6e
commit
9effed12e3
9 changed files with 296 additions and 43 deletions
121
README.en.md
Normal file
121
README.en.md
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# Tablo
|
||||||
|
|
||||||
|
[Русский](README.md) · **English**
|
||||||
|
|
||||||
|
[](https://github.com/EDeev/tablo/actions/workflows/ci.yml)
|
||||||
|
[](https://github.com/EDeev/tablo/actions/workflows/docker.yml)
|
||||||
|
[](https://github.com/EDeev/tablo/releases)
|
||||||
|
|
||||||
|
A web app for students: upload a photo of your timetable, and AI turns it into an editable schedule
|
||||||
|
with per-subject progress trackers you can share by link.
|
||||||
|
|
||||||
|
**Status:** coursework project (2026), completed · live at [tablo.deev.su](https://tablo.deev.su)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Stack:** Python 3.12 · Flask · SQLAlchemy + Alembic · PostgreSQL · OpenAI-compatible Vision API · Playwright · Jinja2 + vanilla JS
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Timetable recognition from a photo or scan
|
||||||
|
- Editing of schedules, subjects and classes; periods may span New Year
|
||||||
|
- 11 tracker types (attendance, grades, deadlines, day streaks, etc.) on a drag-and-drop grid
|
||||||
|
- Tracker generation from a description such as "8 labs and an exam"
|
||||||
|
- Link sharing: view, edit, or copy as a template
|
||||||
|
- Merging several schedules into one, with classes of the same subject combined
|
||||||
|
- Export to JSON, CSV, PNG and a print-ready version
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/EDeev/tablo.git && cd tablo
|
||||||
|
cp .env.example .env # set SECRET_KEY and your AI provider key
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Open `http://localhost:8000`. Compose starts the app and PostgreSQL; migrations run on startup.
|
||||||
|
Prebuilt image: `docker pull ghcr.io/edeev/tablo` or `docker pull dcr.deev.su/edeev/tablo`.
|
||||||
|
|
||||||
|
## Installing without Docker
|
||||||
|
|
||||||
|
Requires Python 3.11+ and PostgreSQL.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv .venv && source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
playwright install --with-deps chromium # for PNG export
|
||||||
|
cp .env.example .env
|
||||||
|
flask --app run db upgrade
|
||||||
|
python run.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `SECRET_KEY` | Flask session signing; the app will not start without it |
|
||||||
|
| `DATABASE_URL` or `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` | PostgreSQL connection |
|
||||||
|
| `OPENAI_API_KEY` | AI provider key |
|
||||||
|
| `OPENAI_BASE_URL` | OpenAI-compatible API URL, if not OpenAI |
|
||||||
|
| `OPENAI_MODEL` | model for recognition and trackers, `gpt-4o` by default |
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Without `OPENAI_API_KEY`, timetable recognition and tracker generation are disabled. Everything else
|
||||||
|
> works: you can create and edit a schedule manually.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
| My schedules | Subject and trackers | Phone |
|
||||||
|
|---|---|---|
|
||||||
|
|  |  |  |
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
B[Browser] --> F[Flask: auth · schedules · subjects · shares · export]
|
||||||
|
F --> S[Services: ai_scan · ai_metrics · merge · export]
|
||||||
|
F --> P[(PostgreSQL)]
|
||||||
|
S --> A[OpenAI-compatible API]
|
||||||
|
S --> C[Chromium via Playwright]
|
||||||
|
```
|
||||||
|
|
||||||
|
Details (in Russian):
|
||||||
|
|
||||||
|
- [docs/architecture.md](docs/architecture.md) — structure, schedule format, trackers, access rules
|
||||||
|
- [docs/deploy.md](docs/deploy.md) — Docker and the production setup
|
||||||
|
- [docs/explanatory_note.pdf](docs/explanatory_note.pdf) — coursework explanatory note
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
[tablo.deev.su](https://tablo.deev.su) runs on a VPS: gunicorn under systemd behind nginx with a
|
||||||
|
Let's Encrypt certificate; PostgreSQL runs on a separate server; AI is Timeweb Cloud AI. GitHub Actions
|
||||||
|
builds the Docker image on every `v*` tag and publishes it to GitHub Packages and to `dcr.deev.su`.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements-dev.txt
|
||||||
|
export TEST_DATABASE_URL=postgresql://tablo:tablo@localhost:5432/tablo_test
|
||||||
|
ruff check . && pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests run against a real PostgreSQL: access rules and export, share links, sign-in and open-redirect
|
||||||
|
protection, period and time parsing, schedule merging. CI starts the database as a service container
|
||||||
|
and runs the same checks on every push.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Coursework project (Moscow Polytechnic University, 2025/26). 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>
|
||||||
130
README.md
130
README.md
|
|
@ -1,77 +1,121 @@
|
||||||
# Tablo
|
# Tablo
|
||||||
|
|
||||||
Веб-приложение для работы с учебным расписанием: распознавание расписания из изображения с помощью ИИ, персональные трекеры успеваемости и совместный доступ по ссылке.
|
**Русский** · [English](README.en.md)
|
||||||
|
|
||||||
Сайт: [tablo.deev.su](https://tablo.deev.su)
|
[](https://github.com/EDeev/tablo/actions/workflows/ci.yml)
|
||||||
|
[](https://github.com/EDeev/tablo/actions/workflows/docker.yml)
|
||||||
|
[](https://github.com/EDeev/tablo/releases)
|
||||||
|
|
||||||
## Функциональность
|
Веб-приложение для студентов: по фото расписания ИИ собирает редактируемое расписание, к каждому
|
||||||
|
предмету можно добавить трекеры успеваемости и поделиться всем по ссылке.
|
||||||
|
|
||||||
- Загрузка фотографии или скана расписания и автоматическое извлечение структурированных данных через AI Vision API
|
**Статус:** учебный проект (курсовая работа, 2026), завершён · работает на [tablo.deev.su](https://tablo.deev.su)
|
||||||
- Полный CRUD для расписаний, предметов и слотов занятий
|
|
||||||
- 11 типов виджетов трекеров успеваемости (посещаемость, оценки, дедлайны, серии дней и др.)
|
|
||||||
- Дашборд с перетаскиваемыми виджетами на сетке 12 колонок
|
|
||||||
- Совместный доступ по токен-ссылке с тремя режимами: просмотр, редактирование, клонирование как шаблон
|
|
||||||
- Объединение нескольких расписаний с разрешением конфликтов по предметам
|
|
||||||
|
|
||||||
## Технологический стек
|

|
||||||
|
|
||||||
- **Backend:** Python, Flask, SQLAlchemy, Flask-Migrate, Flask-Login
|
**Стек:** Python 3.12 · Flask · SQLAlchemy + Alembic · PostgreSQL · OpenAI-совместимый Vision API · Playwright · Jinja2 + vanilla JS
|
||||||
- **База данных:** PostgreSQL (JSONB для хранения структуры расписания)
|
|
||||||
- **ИИ:** Gemini 3.1 Flash через OpenAI-совместимый API (Timeweb Cloud AI)
|
|
||||||
- **Frontend:** Jinja2, Bootstrap 5.3, Vanilla JS, Fetch API
|
|
||||||
|
|
||||||
## Установка
|
## Возможности
|
||||||
|
|
||||||
**Требования:** Python 3.11+, PostgreSQL
|
- Распознавание расписания по фото или скану
|
||||||
|
- Редактирование расписаний, предметов и занятий, периоды с переходом через Новый год
|
||||||
|
- 11 видов трекеров (посещаемость, оценки, дедлайны, серии дней и др.) на сетке с перетаскиванием
|
||||||
|
- Генерация трекера по описанию: «8 лабораторных и экзамен»
|
||||||
|
- Доступ по ссылке: просмотр, редактирование или копия как шаблон
|
||||||
|
- Объединение нескольких расписаний в одно: занятия одного предмета сводятся вместе
|
||||||
|
- Экспорт в JSON, CSV, PNG и версию для печати
|
||||||
|
|
||||||
|
## Быстрый старт
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/EDeev/tablo.git
|
git clone https://github.com/EDeev/tablo.git && cd tablo
|
||||||
cd tablo
|
cp .env.example .env # задайте SECRET_KEY и ключ ИИ-провайдера
|
||||||
python -m venv venv
|
docker compose up -d
|
||||||
venv\Scripts\activate # Windows
|
|
||||||
# source venv/bin/activate # Linux/macOS
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Заполните файл `.env` в корне проекта
|
Откройте `http://localhost:8000`. Compose поднимает приложение и PostgreSQL, миграции применяются
|
||||||
|
при старте. Готовый образ: `docker pull ghcr.io/edeev/tablo` или `docker pull dcr.deev.su/edeev/tablo`.
|
||||||
|
|
||||||
Примените миграции и запустите приложение:
|
## Установка без Docker
|
||||||
|
|
||||||
|
Нужны Python 3.11+ и PostgreSQL.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
flask db upgrade
|
python -m venv .venv && source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
playwright install --with-deps chromium # для экспорта в PNG
|
||||||
|
cp .env.example .env
|
||||||
|
flask --app run db upgrade
|
||||||
python run.py
|
python run.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Приложение будет доступно по адресу `http://localhost:5000`.
|
## Конфигурация
|
||||||
|
|
||||||
## Структура проекта
|
| Переменная | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `SECRET_KEY` | подпись сессий Flask; без неё приложение не запустится |
|
||||||
|
| `DATABASE_URL` или `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` | подключение к PostgreSQL |
|
||||||
|
| `OPENAI_API_KEY` | ключ ИИ-провайдера |
|
||||||
|
| `OPENAI_BASE_URL` | адрес OpenAI-совместимого API, если это не OpenAI |
|
||||||
|
| `OPENAI_MODEL` | модель для распознавания и трекеров, по умолчанию `gpt-4o` |
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Без `OPENAI_API_KEY` не работают распознавание расписаний и генерация трекеров. Всё остальное работает:
|
||||||
|
> расписание можно завести и править вручную.
|
||||||
|
|
||||||
|
## Как выглядит
|
||||||
|
|
||||||
|
| Мои расписания | Предмет и трекеры | Телефон |
|
||||||
|
|---|---|---|
|
||||||
|
|  |  |  |
|
||||||
|
|
||||||
|
## Как устроено
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
B[Браузер] --> F[Flask: auth · schedules · subjects · shares · export]
|
||||||
|
F --> S[Сервисы: ai_scan · ai_metrics · merge · export]
|
||||||
|
F --> P[(PostgreSQL)]
|
||||||
|
S --> A[OpenAI-совместимый API]
|
||||||
|
S --> C[Chromium через Playwright]
|
||||||
```
|
```
|
||||||
tablo/
|
|
||||||
├── app/
|
Подробности:
|
||||||
│ ├── models/ # Модели SQLAlchemy
|
|
||||||
│ ├── routes/ # Блюпринты (auth, schedules, subjects, shares)
|
- [docs/architecture.md](docs/architecture.md) — структура, формат расписания, трекеры, права доступа
|
||||||
│ ├── services/ # Бизнес-логика (AI-распознавание, AI-генерация метрик)
|
- [docs/deploy.md](docs/deploy.md) — Docker и устройство боевого сервера
|
||||||
│ └── templates/ # HTML-шаблоны Jinja2
|
- [docs/explanatory_note.pdf](docs/explanatory_note.pdf) — пояснительная записка к курсовой
|
||||||
├── migrations/ # Файлы миграций Flask-Migrate
|
|
||||||
├── requirements.txt
|
## Развёртывание
|
||||||
└── run.py
|
|
||||||
|
[tablo.deev.su](https://tablo.deev.su) работает на VPS: gunicorn под systemd за nginx с сертификатом
|
||||||
|
Let's Encrypt, PostgreSQL — на отдельном сервере, ИИ — Timeweb Cloud AI. Docker-образ собирает GitHub
|
||||||
|
Actions на каждый тег `v*` и публикует в GitHub Packages и в реестр `dcr.deev.su`.
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements-dev.txt
|
||||||
|
export TEST_DATABASE_URL=postgresql://tablo:tablo@localhost:5432/tablo_test
|
||||||
|
ruff check . && pytest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Тесты идут на настоящем PostgreSQL: права доступа и экспорт, ссылки, вход и защита от открытого
|
||||||
|
редиректа, разбор периодов и времени, слияние расписаний. CI поднимает базу в сервис-контейнере и
|
||||||
|
выполняет то же самое на каждый push.
|
||||||
|
|
||||||
## Лицензия
|
## Лицензия
|
||||||
|
|
||||||
Этот проект является некоммерческим и распространяется под лицензией MIT.
|
Учебный проект (курсовая работа, Московский Политех, 2025/26). Код открыт для изучения, отдельной
|
||||||
|
лицензии нет.
|
||||||
|
|
||||||
## Автор
|
## Автор
|
||||||
|
|
||||||
**Деев Егор Викторович** - Backend Developer
|
**Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space)
|
||||||
- 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>⭐ Если проект оказался полезным, поставьте звездочку на GitHub!</sub>
|
<sub>⭐ Если проект оказался полезным, поставьте звёздочку на GitHub!</sub>
|
||||||
<p><sub>Создано с ❤️ от вашего дорогого - deev.space ©</sub></p>
|
<p><sub>Сделано с ❤️ — <a href="https://deev.space">deev.space</a></sub></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
63
docs/architecture.md
Normal file
63
docs/architecture.md
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Как устроен Tablo
|
||||||
|
|
||||||
|
Flask-приложение с фабрикой `create_app`, блюпринтами и сервисным слоем. База — PostgreSQL
|
||||||
|
(структура расписания хранится в JSON-поле), миграции — Flask-Migrate (Alembic).
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
__init__.py фабрика приложения, расширения (SQLAlchemy, Migrate, Login, CSRF)
|
||||||
|
config.py настройки из окружения
|
||||||
|
access.py права: владелец или редактор по ссылке
|
||||||
|
models/ User, Schedule, SubjectConfig, Metric, Share, ShareEditor
|
||||||
|
routes/ auth, schedules, subjects, shares, export
|
||||||
|
services/
|
||||||
|
ai_scan.py распознавание расписания по фото (vision-модель)
|
||||||
|
ai_metrics.py генерация трекера по текстовому запросу
|
||||||
|
merge.py слияние нескольких расписаний
|
||||||
|
schedule_helpers.py разбор периодов, сборка дня и недели
|
||||||
|
export.py PNG и версия для печати через Playwright/Chromium
|
||||||
|
templates/, static/
|
||||||
|
migrations/ ревизии Alembic
|
||||||
|
tests/ pytest на PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
## Формат расписания
|
||||||
|
|
||||||
|
`Schedule.data` — список предметов. У предмета — типы занятий (лекция, практика…), у типа — цвет и
|
||||||
|
периоды, в периоде — слоты `[день, время, аудитория, преподаватель]`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"subject": "Базы данных",
|
||||||
|
"types": {
|
||||||
|
"Лекция": {"color": "blue", "dates": {"01.09-31.12": [["понедельник", "09:00-10:30", "АВ-301", "Смирнова Е.А."]]}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Период `ДД.ММ-ДД.ММ` может переходить через Новый год (`01.09-03.01`), «Весь период» — без
|
||||||
|
ограничений. Время сортируется как число, поэтому `9:00` и `09:00` равнозначны.
|
||||||
|
|
||||||
|
## Распознавание и трекеры
|
||||||
|
|
||||||
|
`ai_scan.py` отправляет фото и подробный системный промпт в OpenAI-совместимый API и разбирает
|
||||||
|
ответ в JSON указанного формата. `ai_metrics.py` по запросу вида «8 лабораторных и экзамен» подбирает
|
||||||
|
тип трекера и его настройки. Ошибки провайдера пишутся в лог, пользователь видит общее сообщение.
|
||||||
|
|
||||||
|
Типы трекеров: контрольные точки, накопление баллов, счётчик, этапы, чеклист, посещаемость, оценки
|
||||||
|
с весами, дедлайны, серия дней, трудозатраты, история оценок. Трекеры лежат на сетке из 12 колонок,
|
||||||
|
положение и размер сохраняются.
|
||||||
|
|
||||||
|
## Доступ
|
||||||
|
|
||||||
|
| Кто | Что может |
|
||||||
|
|---|---|
|
||||||
|
| владелец | всё, включая экспорт и удаление |
|
||||||
|
| редактор (добавлен в ссылку на редактирование) | просматривать и править по id, экспортировать |
|
||||||
|
| любой со ссылкой на просмотр `/shared/<token>` | только смотреть, без входа |
|
||||||
|
| вошедший пользователь со ссылкой-шаблоном | получить копию расписания себе |
|
||||||
|
|
||||||
|
Проверка прав — одна функция `app/access.py`. Формы защищены CSRF (Flask-WTF), пароли хранятся
|
||||||
|
в виде хешей Werkzeug, переход после входа (`?next=`) — только внутри сайта.
|
||||||
25
docs/deploy.md
Normal file
25
docs/deploy.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Развёртывание
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env # SECRET_KEY и ключ ИИ-провайдера
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Поднимаются приложение и PostgreSQL 16 (данные — в томе `pgdata`), миграции применяются при старте.
|
||||||
|
Готовый образ собирается GitHub Actions на каждый тег `v*`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull ghcr.io/edeev/tablo:latest
|
||||||
|
docker pull dcr.deev.su/edeev/tablo:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Образ большой (около 1,3 ГБ): в нём Chromium для экспорта расписания в PNG.
|
||||||
|
|
||||||
|
## Как работает tablo.deev.su
|
||||||
|
|
||||||
|
- gunicorn (2 воркера) под systemd за nginx с сертификатом Let's Encrypt;
|
||||||
|
- PostgreSQL — на отдельном сервере, подключение через `DB_*`;
|
||||||
|
- ИИ — Timeweb Cloud AI (OpenAI-совместимый endpoint), модель задаётся `OPENAI_MODEL`;
|
||||||
|
- обновление: выложить код, `pip install -r requirements.txt`, `flask db upgrade`, перезапуск юнита.
|
||||||
BIN
docs/screenshots/mobile.png
Normal file
BIN
docs/screenshots/mobile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
BIN
docs/screenshots/profile.png
Normal file
BIN
docs/screenshots/profile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/screenshots/schedule.png
Normal file
BIN
docs/screenshots/schedule.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
docs/screenshots/subjects.png
Normal file
BIN
docs/screenshots/subjects.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
Loading…
Add table
Reference in a new issue