feat(db): миграции групп, курсов, статусов и профиля
This commit is contained in:
52
deploy/preflight/README.md
Normal file
52
deploy/preflight/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Preflight перед миграциями групп и курсов
|
||||
|
||||
**Перед любыми миграциями обязателен backup:** `./deploy/backup.sh backup`
|
||||
(см. [`../BACKUP.md`](../BACKUP.md)). Preflight не заменяет backup.
|
||||
|
||||
Два скрипта только читают данные:
|
||||
|
||||
- `groups-courses.sql` — до миграций: проблемы, которые могут помешать;
|
||||
- `groups-courses-post.sql` — после миграций и bootstrap курса C: проверка результата.
|
||||
|
||||
## До миграций
|
||||
|
||||
```bash
|
||||
psql -h localhost -U lms -d lms -f deploy/preflight/groups-courses.sql
|
||||
```
|
||||
|
||||
Проверяет:
|
||||
|
||||
- группы, имя которых не разбирается шаблоном;
|
||||
- одинаковые кортежи `(stream, degree, number, course, enrollment_year)`;
|
||||
- группы без полного набора полей;
|
||||
- одинаковые имена групп;
|
||||
- студентов без текущей группы;
|
||||
- дубли номеров лабораторных при переносе в один курс;
|
||||
- дубли slug глав;
|
||||
- объём материалов и активов.
|
||||
|
||||
## После миграций и bootstrap
|
||||
|
||||
```bash
|
||||
psql -h localhost -U lms -d lms -f deploy/preflight/groups-courses-post.sql
|
||||
```
|
||||
|
||||
Проверяет:
|
||||
|
||||
- лабы без курса (должно быть `0` перед `SET NOT NULL`);
|
||||
- главы без курса (должно быть `0`, если все текущие материалы курсовые);
|
||||
- наличие ровно одного курса C;
|
||||
- комментарии без автора;
|
||||
- статусы аккаунтов;
|
||||
- группы без полного разбора.
|
||||
|
||||
## Что делать с результатами
|
||||
|
||||
1. Группы с нестандартными именами — разобрать вручную и заполнить поля до
|
||||
перехода на `NOT NULL`.
|
||||
2. Одинаковые кортежи — исправить номер или год набора.
|
||||
3. Дубли номеров лаб — перенумеровать или разнести по разным курсам до
|
||||
включения `UNIQUE (number, course_id)` / `NOT NULL`.
|
||||
4. Дубли slug — переименовать главы до частичных индексов.
|
||||
5. После bootstrap курса C проверить, что `labs_without_course = 0` и
|
||||
`chapters_without_course = 0`.
|
||||
38
deploy/preflight/groups-courses-post.sql
Normal file
38
deploy/preflight/groups-courses-post.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- Пост-миграционные проверки. Запускать после миграций и bootstrap курса C.
|
||||
\pset pager off
|
||||
|
||||
\echo '=== 1. Лабы без курса (должно быть 0 перед SET NOT NULL) ==='
|
||||
SELECT count(*) AS labs_without_course
|
||||
FROM academic.labs
|
||||
WHERE course_id IS NULL;
|
||||
|
||||
\echo '=== 2. Главы без курса (должно быть 0, если все текущие материалы курсовые) ==='
|
||||
SELECT count(*) AS chapters_without_course
|
||||
FROM academic.chapters
|
||||
WHERE course_id IS NULL;
|
||||
|
||||
\echo '=== 3. Курсы (должен быть ровно один курс C) ==='
|
||||
SELECT id, course_no, title, owner_account_id, status
|
||||
FROM academic.courses
|
||||
ORDER BY course_no;
|
||||
|
||||
\echo '=== 4. Комментарии без автора (старые записи) ==='
|
||||
SELECT count(*) AS comments_without_author
|
||||
FROM progress.grade_comments
|
||||
WHERE author_account_id IS NULL;
|
||||
|
||||
\echo '=== 5. Аккаунты по ролям и статусам ==='
|
||||
SELECT role, status, count(*) AS accounts
|
||||
FROM identity.accounts
|
||||
GROUP BY role, status
|
||||
ORDER BY role, status;
|
||||
|
||||
\echo '=== 6. Группы без полного разбора ==='
|
||||
SELECT id, name, stream, course, number, degree, enrollment_year
|
||||
FROM identity.groups
|
||||
WHERE stream IS NULL
|
||||
OR course IS NULL
|
||||
OR number IS NULL
|
||||
OR degree IS NULL
|
||||
OR enrollment_year IS NULL
|
||||
ORDER BY name;
|
||||
124
deploy/preflight/groups-courses.sql
Normal file
124
deploy/preflight/groups-courses.sql
Normal file
@@ -0,0 +1,124 @@
|
||||
-- Preflight (до миграций) для перехода на группы-когорты, курсы и перенос лаб/материалов.
|
||||
-- Только чтение. Запускать на копии рабочей БД до применения миграций.
|
||||
-- Пример:
|
||||
-- psql -h localhost -U lms -d lms -f deploy/preflight/groups-courses.sql
|
||||
|
||||
\pset pager off
|
||||
\echo '=== 1. Группы, не подходящие под шаблон ^(.+)-([0-9])([0-9]+)(БО|МО)$ ==='
|
||||
SELECT id, name
|
||||
FROM identity.groups
|
||||
WHERE name !~ '^(.+)-([0-9])([0-9]+)(БО|МО)$'
|
||||
ORDER BY name;
|
||||
|
||||
\echo '=== 2. Разобранные группы с одинаковым кортежем (stream, degree, number, course, enrollment_year) ==='
|
||||
WITH params AS (
|
||||
SELECT CASE
|
||||
WHEN EXTRACT(MONTH FROM now()) >= 9 THEN EXTRACT(YEAR FROM now())
|
||||
ELSE EXTRACT(YEAR FROM now()) - 1
|
||||
END::int AS academic_year_start
|
||||
),
|
||||
parsed AS (
|
||||
SELECT
|
||||
g.name,
|
||||
m[1] AS stream,
|
||||
m[2]::int AS course,
|
||||
m[3]::int AS number,
|
||||
m[4] AS degree,
|
||||
(SELECT academic_year_start FROM params) - (m[2]::int - 1) AS enrollment_year
|
||||
FROM identity.groups g
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT regexp_match(g.name, '^(.+)-([0-9])([0-9]+)(БО|МО)$') AS m
|
||||
) parsed_name
|
||||
WHERE m IS NOT NULL
|
||||
)
|
||||
SELECT stream, degree, number, course, enrollment_year,
|
||||
count(*) AS groups,
|
||||
array_agg(name ORDER BY name) AS names
|
||||
FROM parsed
|
||||
GROUP BY stream, degree, number, course, enrollment_year
|
||||
HAVING count(*) > 1
|
||||
ORDER BY stream, degree, course, number;
|
||||
|
||||
\echo '=== 3. Группы, которые не разберутся шаблоном (потребуют ручного разбора) ==='
|
||||
WITH parsed AS (
|
||||
SELECT
|
||||
g.id,
|
||||
g.name,
|
||||
m[1] AS stream,
|
||||
m[2]::int AS course,
|
||||
m[3]::int AS number,
|
||||
m[4] AS degree
|
||||
FROM identity.groups g
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT regexp_match(g.name, '^(.+)-([0-9])([0-9]+)(БО|МО)$') AS m
|
||||
) parsed_name
|
||||
)
|
||||
SELECT id, name, stream, course, number, degree
|
||||
FROM parsed
|
||||
WHERE stream IS NULL
|
||||
OR course IS NULL
|
||||
OR number IS NULL
|
||||
OR degree IS NULL
|
||||
ORDER BY name;
|
||||
|
||||
\echo '=== 4. Одинаковые имена групп (после снятия UNIQUE станут допустимы) ==='
|
||||
SELECT name, count(*) AS groups
|
||||
FROM identity.groups
|
||||
GROUP BY name
|
||||
HAVING count(*) > 1
|
||||
ORDER BY name;
|
||||
|
||||
\echo '=== 5. Студенты без текущей группы ==='
|
||||
SELECT s.student_no, a.login, s.last_name, s.first_name
|
||||
FROM identity.students s
|
||||
JOIN identity.accounts a ON a.id = s.account_id
|
||||
LEFT JOIN identity.enrollments e ON e.student_id = s.account_id
|
||||
WHERE e.id IS NULL
|
||||
ORDER BY s.student_no;
|
||||
|
||||
\echo '=== 6. Аккаунты по ролям (до появления статусов) ==='
|
||||
SELECT role, count(*) AS accounts
|
||||
FROM identity.accounts
|
||||
GROUP BY role
|
||||
ORDER BY role;
|
||||
|
||||
\echo '=== 7. Дубли номеров лабораторных (все лабы переезжают в один курс C) ==='
|
||||
SELECT number,
|
||||
count(*) AS labs,
|
||||
array_agg(id ORDER BY created_at) AS ids,
|
||||
array_agg(title ORDER BY created_at) AS titles
|
||||
FROM academic.labs
|
||||
GROUP BY number
|
||||
HAVING count(*) > 1
|
||||
ORDER BY number;
|
||||
|
||||
\echo '=== 8. Лабораторные по семестрам ==='
|
||||
SELECT semester_id, number, count(*) AS labs
|
||||
FROM academic.labs
|
||||
GROUP BY semester_id, number
|
||||
HAVING count(*) > 1
|
||||
ORDER BY semester_id, number;
|
||||
|
||||
\echo '=== 9. Дубли slug глав (до перехода на частичные индексы) ==='
|
||||
SELECT slug, count(*) AS chapters
|
||||
FROM academic.chapters
|
||||
GROUP BY slug
|
||||
HAVING count(*) > 1
|
||||
ORDER BY slug;
|
||||
|
||||
\echo '=== 10. Объём материалов ==='
|
||||
SELECT
|
||||
(SELECT count(*) FROM academic.chapters) AS chapters,
|
||||
(SELECT count(*) FROM academic.sections) AS sections,
|
||||
(SELECT count(*) FROM academic.articles) AS articles,
|
||||
(SELECT count(*) FROM academic.article_assets) AS article_assets;
|
||||
|
||||
\echo '=== 11. Активы без статьи ==='
|
||||
SELECT count(*) AS assets_without_article
|
||||
FROM academic.article_assets
|
||||
WHERE article_id IS NULL;
|
||||
|
||||
\echo '=== 12. Семестры ==='
|
||||
SELECT id, title, year, active
|
||||
FROM academic.semesters
|
||||
ORDER BY year, title;
|
||||
Reference in New Issue
Block a user