Перенос в master всего, кроме админки: ТОП-группы, порядок групп, запуск без Docker
Админка осталась отдельной ветвью admin-panel; в master её нет (ни кода, ни страниц, ни переменных окружения, ни тома настроек). Что перенесено: - parsing: имена групп вида «ИВТ-ТОП-11БО» распознаются, порядок групп = порядок столбцов таблицы (похожие заголовки с пробелами группой не считаются); - main: GET /groups отдаёт группы в порядке столбцов; интервал обновления из SCHEDULE_INTERVAL_MINUTES (со старым SCHEDULE_CACHE_TTL как fallback); - cache: по интервалу сверяем modified — если файл не менялся, не скачиваем; при загрузке считаем sha256 и пишем в лог и /status, изменился ли файл; - source: запросы к Яндекс.Диску с no-cache и меткой времени; - frontend: ответы API с Cache-Control: no-store, общий .env из корня проекта (и backend, и frontend), страницы/прокси админки нет; - Dockerfile/compose/.env.example: без ADMIN_* и без тома настроек; в api-образ попадают все модули backend (проверяется тестом test_deploy); - Makefile: make help/venv/run/run-api/run-front/test + docker-цели — «make run» поднимает всё локально без Docker; - README: быстрый старт, полный список зависимостей, запуск локально и в Docker; - deploy/: пример nginx и инструкция «второй стенд рядом с основным». Тесты: 114 (удалены админские), все зелёные.
This commit is contained in:
108
README.md
108
README.md
@@ -4,6 +4,18 @@
|
||||
университета (файл xlsx), который скачивается, разбирается, превращается в JSON
|
||||
API и отображается на фронте. Деплой: `schedule.our-place.ru`.
|
||||
|
||||
## Быстрый старт (без Docker)
|
||||
|
||||
Нужен только Python 3.11+ и публичная ссылка на таблицу.
|
||||
|
||||
```bash
|
||||
cp .env.example .env # в SCHEDULE_LINK вписать публичную ссылку Я.Диска
|
||||
make run # создаст .venv, поставит зависимости и запустит оба сервиса
|
||||
```
|
||||
|
||||
Открыть <http://127.0.0.1:8080>. Остановить — `Ctrl+C`.
|
||||
Подробнее — разделы «Зависимости» и «Запуск» ниже.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```
|
||||
@@ -98,7 +110,7 @@ src/
|
||||
source.py скачивание файла по публичной ссылке Я.Диска
|
||||
lessons.py разбор ячеек занятий (общий конвейер ролей)
|
||||
parsing.py лист → расписание, недели, индексы преподавателей/аудиторий
|
||||
cache.py ScheduleCache: блокировка, TTL, modified, stale-while-error
|
||||
cache.py ScheduleCache: блокировка, интервал, stale-while-error
|
||||
errors.py ScheduleError
|
||||
frontend/
|
||||
main.py FastAPI: статика + прокси /api/* (таймаут 150 c)
|
||||
@@ -110,6 +122,9 @@ src/
|
||||
(app-icon.svg, icon-192/512.png, apple-touch-icon.png, favicon-32.png)
|
||||
tools/
|
||||
make_icons.py генерация иконок приложения из логотипа (нужен rsvg-convert)
|
||||
deploy/
|
||||
nginx-dev.conf пример сайта nginx для отдельного стенда
|
||||
README-dev.md как поднять второй стенд рядом с основным
|
||||
tests/
|
||||
conftest.py общие фикстуры
|
||||
xlsx_builder.py генерация xlsx в памяти (без openpyxl)
|
||||
@@ -117,7 +132,8 @@ tests/
|
||||
test_lessons.py все форматы ячеек
|
||||
test_xlsx.py чтение книги
|
||||
test_parsing.py разбор листов, недели, индексы
|
||||
test_api.py API и поведение кэша (TTL/modified/stale, 404/400/502)
|
||||
test_api.py API и поведение кэша (интервал/stale, 404/400/502)
|
||||
test_deploy.py проверка, что образы копируют все нужные файлы
|
||||
```
|
||||
|
||||
## API
|
||||
@@ -150,19 +166,84 @@ tests/
|
||||
| Переменная | По умолчанию | Где | Описание |
|
||||
|---|---|---|---|
|
||||
| `SCHEDULE_LINK` | — | api | публичная ссылка Я.Диска, **обязательна** |
|
||||
| `SCHEDULE_CACHE_TTL` | `900` | api | время жизни кэша, сек |
|
||||
| `SCHEDULE_INTERVAL_MINUTES` | `15` | api | как часто перечитывать таблицу, минут |
|
||||
| `SCHEDULE_CACHE_TTL` | `900` | api | устаревшее: интервал в секундах (если не задан предыдущий) |
|
||||
| `SCHEDULE_RETRY_AFTER` | `60` | api | пауза между попытками при сбое источника |
|
||||
| `SCHEDULE_API` | `http://127.0.0.1:8000` | frontend | адрес бэкенда (в docker: `http://api:8000`) |
|
||||
| `SCHEDULE_PROXY_TIMEOUT` | `150` | frontend | таймаут прокси к бэкенду, сек |
|
||||
| `API_PORT` | `8000` | docker | внешний порт api |
|
||||
| `FRONTEND_PORT` | `8080` | docker | внешний порт frontend (на проде: `127.0.0.1:8090`) |
|
||||
|
||||
`.env` ищется вверх от каталога модуля (backend — до корня репозитория);
|
||||
`load_dotenv` не перезаписывает уже выставленные переменные окружения.
|
||||
`.env` ищется вверх от каталога модуля: и backend, и frontend находят общий
|
||||
`.env` в корне репозитория; `load_dotenv` не перезаписывает уже выставленные
|
||||
переменные окружения.
|
||||
|
||||
## Зависимости
|
||||
|
||||
Проект состоит из двух сервисов на Python (FastAPI): `api` и `frontend`.
|
||||
Всё ставится одной командой `make venv` (или вручную, см. ниже).
|
||||
|
||||
**Системные требования**
|
||||
|
||||
| Что | Зачем |
|
||||
|---|---|
|
||||
| Python 3.11+ (проверено на 3.12 и 3.14) | оба сервиса |
|
||||
| Docker + docker compose v2 | только для запуска в контейнерах (опционально) |
|
||||
| `rsvg-convert` (пакет `librsvg2-bin`) | только для пересборки иконок `tools/make_icons.py` |
|
||||
|
||||
**Python-зависимости** — закреплены в `requirements.txt` (рабочие) и
|
||||
`requirements-dev.txt` (+ тесты):
|
||||
|
||||
| Пакет | Версия | Назначение |
|
||||
|---|---|---|
|
||||
| fastapi | 0.141.1 | веб-фреймворк обоих сервисов |
|
||||
| uvicorn | 0.52.4 | ASGI-сервер, которым запускаются сервисы |
|
||||
| python-dotenv | 1.2.3 | чтение `.env` |
|
||||
| requests | 2.34.2 | скачивание файла с Яндекс.Диска и прокси к API |
|
||||
| pytest | 9.1.1 | тесты (`make test`) — только dev |
|
||||
| httpx2 | 2.12.0 | HTTP-клиент для `TestClient` в тестах — только dev |
|
||||
|
||||
Ничего, кроме этого, не нужно: xlsx разбирается стандартной библиотекой
|
||||
(`zipfile` + `xml.etree`), базы данных нет, кэш — в памяти процесса.
|
||||
|
||||
## Запуск
|
||||
|
||||
### Продакшен (Docker)
|
||||
### Локально, без Docker (обычный компьютер)
|
||||
|
||||
```bash
|
||||
git clone <адрес репозитория> && cd Schedule
|
||||
cp .env.example .env # открыть .env и вписать SCHEDULE_LINK
|
||||
make run # = make venv + запуск api и сайта
|
||||
```
|
||||
|
||||
`make run` создаёт виртуальное окружение `.venv`, ставит зависимости и
|
||||
поднимает два сервиса с авто-перезагрузкой:
|
||||
|
||||
- API — <http://127.0.0.1:8000> (например, `/status` и `/groups`);
|
||||
- сайт — <http://127.0.0.1:8080> (его и открывать в браузере).
|
||||
|
||||
Остановить — `Ctrl+C`. Данные тянутся из интернета при первом запросе
|
||||
(скачивание занимает несколько секунд), дальше обновляются по интервалу.
|
||||
|
||||
Полезные команды (`make help` — весь список):
|
||||
|
||||
| Команда | Что делает |
|
||||
|---|---|
|
||||
| `make venv` | создать `.venv` и установить зависимости |
|
||||
| `make run` (или `make dev`) | запустить API и сайт |
|
||||
| `make run-api` / `make run-front` | запустить только один из сервисов |
|
||||
| `make test` | прогнать тесты |
|
||||
|
||||
Если хочется без make — то же самое руками:
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -r requirements-dev.txt
|
||||
.venv/bin/python -m uvicorn main:app --reload --port 8000 # в каталоге src/backend
|
||||
.venv/bin/python -m uvicorn main:app --reload --port 8080 # в каталоге src/frontend
|
||||
```
|
||||
|
||||
### В Docker (продакшен)
|
||||
|
||||
```bash
|
||||
cp .env.example .env # вписать SCHEDULE_LINK (и порты при необходимости)
|
||||
@@ -172,18 +253,10 @@ cp .env.example .env # вписать SCHEDULE_LINK (и порты при
|
||||
У обоих сервисов есть healthcheck на `/healthz`; frontend стартует после того,
|
||||
как api стал healthy. Обновление: `git pull && docker compose up -d --build`.
|
||||
|
||||
### Разработка
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -r requirements-dev.txt
|
||||
make dev # api :8000 и фронт :8080, оба с --reload
|
||||
```
|
||||
|
||||
### Тесты
|
||||
|
||||
```bash
|
||||
make test # 104 теста, сеть не нужна
|
||||
make test # 114 тестов, сеть не нужна
|
||||
```
|
||||
|
||||
## Развёртывание на сервере
|
||||
@@ -206,8 +279,9 @@ nginx-сайт `schedule.our-place.ru` проксирует 443 → 127.0.0.1:80
|
||||
|
||||
## Замечания
|
||||
|
||||
- Принудительное обновление таблицы (пропуск кэша, `force`) из API намеренно
|
||||
убрано; планируется вернуть вместе с авторизацией и ролью администратора.
|
||||
- Публичного принудительного обновления в API нет: расписание обновляется по
|
||||
интервалу (`SCHEDULE_INTERVAL_MINUTES`), а если таблица не менялась,
|
||||
скачивание не повторяется (сверяется `modified`).
|
||||
- Кэш живёт в памяти единственного воркера: при горизонтальном масштабировании
|
||||
api его придётся выносить в общее хранилище.
|
||||
- Строки внутри дня в таблице строго чередуются «числитель / знаменатель».
|
||||
|
||||
Reference in New Issue
Block a user