diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..4359291 --- /dev/null +++ b/README.en.md @@ -0,0 +1,121 @@ +# Tablo + +[Русский](README.md) · **English** + +[![CI](https://github.com/EDeev/tablo/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/tablo/actions/workflows/ci.yml) +[![Docker](https://github.com/EDeev/tablo/actions/workflows/docker.yml/badge.svg)](https://github.com/EDeev/tablo/actions/workflows/docker.yml) +[![Release](https://img.shields.io/github/v/release/EDeev/tablo)](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) + +![Weekly schedule and trackers](docs/screenshots/schedule.png) + +**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 | +|---|---|---| +| ![My schedules](docs/screenshots/profile.png) | ![Subject and trackers](docs/screenshots/subjects.png) | ![Phone](docs/screenshots/mobile.png) | + +## 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) + +--- + +
+ ⭐ If you find this project useful, give it a star on GitHub! +

Made with ❤️ — deev.space

+
diff --git a/README.md b/README.md index e2fbd69..e581947 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,121 @@ # Tablo -Веб-приложение для работы с учебным расписанием: распознавание расписания из изображения с помощью ИИ, персональные трекеры успеваемости и совместный доступ по ссылке. +**Русский** · [English](README.en.md) -Сайт: [tablo.deev.su](https://tablo.deev.su) +[![CI](https://github.com/EDeev/tablo/actions/workflows/ci.yml/badge.svg)](https://github.com/EDeev/tablo/actions/workflows/ci.yml) +[![Docker](https://github.com/EDeev/tablo/actions/workflows/docker.yml/badge.svg)](https://github.com/EDeev/tablo/actions/workflows/docker.yml) +[![Release](https://img.shields.io/github/v/release/EDeev/tablo)](https://github.com/EDeev/tablo/releases) -## Функциональность +Веб-приложение для студентов: по фото расписания ИИ собирает редактируемое расписание, к каждому +предмету можно добавить трекеры успеваемости и поделиться всем по ссылке. -- Загрузка фотографии или скана расписания и автоматическое извлечение структурированных данных через AI Vision API -- Полный CRUD для расписаний, предметов и слотов занятий -- 11 типов виджетов трекеров успеваемости (посещаемость, оценки, дедлайны, серии дней и др.) -- Дашборд с перетаскиваемыми виджетами на сетке 12 колонок -- Совместный доступ по токен-ссылке с тремя режимами: просмотр, редактирование, клонирование как шаблон -- Объединение нескольких расписаний с разрешением конфликтов по предметам +**Статус:** учебный проект (курсовая работа, 2026), завершён · работает на [tablo.deev.su](https://tablo.deev.su) -## Технологический стек +![Расписание на неделю и трекеры](docs/screenshots/schedule.png) -- **Backend:** Python, Flask, SQLAlchemy, Flask-Migrate, Flask-Login -- **База данных:** PostgreSQL (JSONB для хранения структуры расписания) -- **ИИ:** Gemini 3.1 Flash через OpenAI-совместимый API (Timeweb Cloud AI) -- **Frontend:** Jinja2, Bootstrap 5.3, Vanilla JS, Fetch API +**Стек:** Python 3.12 · Flask · SQLAlchemy + Alembic · PostgreSQL · OpenAI-совместимый Vision API · Playwright · Jinja2 + vanilla JS -## Установка +## Возможности -**Требования:** Python 3.11+, PostgreSQL +- Распознавание расписания по фото или скану +- Редактирование расписаний, предметов и занятий, периоды с переходом через Новый год +- 11 видов трекеров (посещаемость, оценки, дедлайны, серии дней и др.) на сетке с перетаскиванием +- Генерация трекера по описанию: «8 лабораторных и экзамен» +- Доступ по ссылке: просмотр, редактирование или копия как шаблон +- Объединение нескольких расписаний в одно: занятия одного предмета сводятся вместе +- Экспорт в JSON, CSV, PNG и версию для печати + +## Быстрый старт ```bash -git clone https://github.com/EDeev/tablo.git -cd tablo -python -m venv venv -venv\Scripts\activate # Windows -# source venv/bin/activate # Linux/macOS -pip install -r requirements.txt +git clone https://github.com/EDeev/tablo.git && cd tablo +cp .env.example .env # задайте SECRET_KEY и ключ ИИ-провайдера +docker compose up -d ``` -Заполните файл `.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 -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 ``` -Приложение будет доступно по адресу `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` не работают распознавание расписаний и генерация трекеров. Всё остальное работает: +> расписание можно завести и править вручную. + +## Как выглядит + +| Мои расписания | Предмет и трекеры | Телефон | +|---|---|---| +| ![Мои расписания](docs/screenshots/profile.png) | ![Предмет и трекеры](docs/screenshots/subjects.png) | ![Телефон](docs/screenshots/mobile.png) | + +## Как устроено + +```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) -│ ├── services/ # Бизнес-логика (AI-распознавание, AI-генерация метрик) -│ └── templates/ # HTML-шаблоны Jinja2 -├── migrations/ # Файлы миграций Flask-Migrate -├── requirements.txt -└── run.py + +Подробности: + +- [docs/architecture.md](docs/architecture.md) — структура, формат расписания, трекеры, права доступа +- [docs/deploy.md](docs/deploy.md) — Docker и устройство боевого сервера +- [docs/explanatory_note.pdf](docs/explanatory_note.pdf) — пояснительная записка к курсовой + +## Развёртывание + +[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: [@EDeev](https://github.com/EDeev) -- Email: egor@deev.space -- Telegram: [@Egor_Deev](https://t.me/Egor_Deev) +**Деев Егор Викторович** — [GitHub](https://github.com/EDeev) · [Telegram](https://t.me/DeevEgor) · [egor@deev.space](mailto:egor@deev.space) ---
- ⭐ Если проект оказался полезным, поставьте звездочку на GitHub! -

Создано с ❤️ от вашего дорогого - deev.space ©

+ ⭐ Если проект оказался полезным, поставьте звёздочку на GitHub! +

Сделано с ❤️ — deev.space

diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..402eaac --- /dev/null +++ b/docs/architecture.md @@ -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/` | только смотреть, без входа | +| вошедший пользователь со ссылкой-шаблоном | получить копию расписания себе | + +Проверка прав — одна функция `app/access.py`. Формы защищены CSRF (Flask-WTF), пароли хранятся +в виде хешей Werkzeug, переход после входа (`?next=`) — только внутри сайта. diff --git a/docs/deploy.md b/docs/deploy.md new file mode 100644 index 0000000..26f8741 --- /dev/null +++ b/docs/deploy.md @@ -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`, перезапуск юнита. diff --git a/explanatory_note.pdf b/docs/explanatory_note.pdf similarity index 100% rename from explanatory_note.pdf rename to docs/explanatory_note.pdf diff --git a/docs/screenshots/mobile.png b/docs/screenshots/mobile.png new file mode 100644 index 0000000..e1bc442 Binary files /dev/null and b/docs/screenshots/mobile.png differ diff --git a/docs/screenshots/profile.png b/docs/screenshots/profile.png new file mode 100644 index 0000000..34f9b79 Binary files /dev/null and b/docs/screenshots/profile.png differ diff --git a/docs/screenshots/schedule.png b/docs/screenshots/schedule.png new file mode 100644 index 0000000..c92a979 Binary files /dev/null and b/docs/screenshots/schedule.png differ diff --git a/docs/screenshots/subjects.png b/docs/screenshots/subjects.png new file mode 100644 index 0000000..0abd305 Binary files /dev/null and b/docs/screenshots/subjects.png differ