Merge branch 'Redesign' of https://gitea.zuev.company/Zuev/magistr into Redesign
This commit is contained in:
22
docs/API.md
22
docs/API.md
@@ -902,7 +902,7 @@ API возвращает `409 Conflict`; соседние интервалы и
|
|||||||
|
|
||||||
| Метод | URL | Назначение |
|
| Метод | URL | Назначение |
|
||||||
|-------|-----|------------|
|
|-------|-----|------------|
|
||||||
| `GET` | `/api/edu-office/schedule/quality?semesterId=&versionId=` | Итоговая оценка, метрики и объяснимый список проблем выбранной версии |
|
| `GET` | `/api/edu-office/schedule/quality?semesterId=&versionId=` | Итоговая оценка, метрики и объяснимый список проблемных правил выбранной версии |
|
||||||
| `GET` | `/api/edu-office/schedule/quality/recommendations?semesterId=&versionId=&scheduleRuleSlotId=&lessonDate=` | Проверенные локальные варианты улучшения опубликованного занятия |
|
| `GET` | `/api/edu-office/schedule/quality/recommendations?semesterId=&versionId=&scheduleRuleSlotId=&lessonDate=` | Проверенные локальные варианты улучшения опубликованного занятия |
|
||||||
|
|
||||||
Доступ имеют только `ADMIN` и `EDUCATION_OFFICE`. `versionId` необязателен: без него
|
Доступ имеют только `ADMIN` и `EDUCATION_OFFICE`. `versionId` необязателен: без него
|
||||||
@@ -929,7 +929,7 @@ override текущей публикации. Для них доступны о
|
|||||||
"score": 84,
|
"score": 84,
|
||||||
"rawPenalty": 17,
|
"rawPenalty": 17,
|
||||||
"lessonCount": 36,
|
"lessonCount": 36,
|
||||||
"totalProblemCount": 5,
|
"totalProblemCount": 1,
|
||||||
"problemsTruncated": false,
|
"problemsTruncated": false,
|
||||||
"metrics": [
|
"metrics": [
|
||||||
{
|
{
|
||||||
@@ -943,11 +943,16 @@ override текущей публикации. Для них доступны о
|
|||||||
],
|
],
|
||||||
"problems": [
|
"problems": [
|
||||||
{
|
{
|
||||||
"id": "GROUP_GAP:2026-09-14:31:18",
|
"id": "RULE:24",
|
||||||
"type": "GROUP_GAP",
|
"type": "GROUP_GAP",
|
||||||
"severity": "MEDIUM",
|
"severity": "MEDIUM",
|
||||||
"title": "Окно в расписании группы",
|
"title": "Правило «Архитектура систем» снижает качество",
|
||||||
"penalty": 2,
|
"description": "3 занятия этого правила снижают оценку: Окно в расписании группы; Избыточно крупная аудитория",
|
||||||
|
"penalty": 8,
|
||||||
|
"scheduleRuleId": 24,
|
||||||
|
"affectedLessonCount": 3,
|
||||||
|
"problemTypes": ["GROUP_GAP", "ROOM_OVERSIZED"],
|
||||||
|
"issueLabels": ["Окно в расписании группы", "Избыточно крупная аудитория"],
|
||||||
"scheduleRuleSlotId": 31,
|
"scheduleRuleSlotId": 31,
|
||||||
"lessonDate": "2026-09-14",
|
"lessonDate": "2026-09-14",
|
||||||
"optimizable": true
|
"optimizable": true
|
||||||
@@ -957,8 +962,11 @@ override текущей публикации. Для них доступны о
|
|||||||
```
|
```
|
||||||
|
|
||||||
`score` находится в диапазоне от 0 до 100 и нормализует сумму объяснимых штрафов
|
`score` находится в диапазоне от 0 до 100 и нормализует сумму объяснимых штрафов
|
||||||
относительно числа занятий. Поле `totalProblemCount` содержит полный размер результата,
|
относительно числа занятий. Нарушения сначала рассчитываются для каждого фактического
|
||||||
а массив `problems` ограничен 300 элементами; `problemsTruncated` сообщает об усечении.
|
занятия, затем объединяются по `scheduleRuleId`: `penalty` хранит сумму всех штрафов правила,
|
||||||
|
`affectedLessonCount` — число затронутых занятий, `problemTypes` и `issueLabels` — все его
|
||||||
|
категории и объяснения. Поле `totalProblemCount` содержит число проблемных правил, а массив
|
||||||
|
`problems` ограничен 300 элементами; `problemsTruncated` сообщает об усечении.
|
||||||
|
|
||||||
Рекомендации подбирают другой эффективный временной слот того же учебного дня либо
|
Рекомендации подбирают другой эффективный временной слот того же учебного дня либо
|
||||||
подходящую активную аудиторию. Занятия, уже закреплённые ручным override, исключаются.
|
подходящую активную аудиторию. Занятия, уже закреплённые ручным override, исключаются.
|
||||||
|
|||||||
@@ -447,9 +447,11 @@ constraint. При чтении API разворачивает периоды о
|
|||||||
Неравномерность дневной нагрузки выводится отдельной диагностической метрикой в процентах
|
Неравномерность дневной нагрузки выводится отдельной диагностической метрикой в процентах
|
||||||
и не добавляет скрытого штрафа к итоговой оценке.
|
и не добавляет скрытого штрафа к итоговой оценке.
|
||||||
|
|
||||||
В ответе сохраняются исходный штраф, вклад каждого критерия и конкретные проблемы с
|
Внутренне штраф рассчитывается для каждого фактического занятия, поэтому повторяющиеся
|
||||||
датой, занятием и затронутой сущностью. Это делает оценку воспроизводимой и позволяет
|
пары одного правила увеличивают его вклад в оценку. Перед выдачей нарушения объединяются
|
||||||
фильтровать проблемы по типу и серьёзности.
|
по правилу: одна карточка содержит суммарный штраф, число затронутых занятий, все типы и
|
||||||
|
краткие причины. Это сохраняет воспроизводимость оценки, но направляет пользователя к
|
||||||
|
изменению базового правила вместо ручного исправления каждой пары.
|
||||||
|
|
||||||
Для проблемы опубликованной версии помощник перебирает другие слоты эффективной сетки того же дня и
|
Для проблемы опубликованной версии помощник перебирает другие слоты эффективной сетки того же дня и
|
||||||
активные аудитории достаточной вместимости. Каждый вариант повторно проходит
|
активные аудитории достаточной вместимости. Каждый вариант повторно проходит
|
||||||
@@ -457,9 +459,10 @@ constraint. При чтении API разворачивает периоды о
|
|||||||
показывает улучшения и компромиссы. Занятия с ручным override считаются закреплёнными и не
|
показывает улучшения и компромиссы. Занятия с ручным override считаются закреплёнными и не
|
||||||
получают рекомендаций. Применение возможно только после подтверждения пользователя через
|
получают рекомендаций. Применение возможно только после подтверждения пользователя через
|
||||||
обычный механизм `schedule_overrides`; автоматической публикации и полного solver в MVP
|
обычный механизм `schedule_overrides`; автоматической публикации и полного solver в MVP
|
||||||
нет. Для черновика доступны те же оценка, метрики и объяснимые проблемы, но рекомендации
|
нет. Интерфейс анализа для опубликованной версии и черновика открывает выбранное правило
|
||||||
не создаются: пользователь исправляет правила в изолированном конструкторе и повторяет
|
в конструкторе соответствующей версии; черновик остаётся изолированным до публикации.
|
||||||
проверку до публикации. Архив анализируется только для чтения.
|
Архив анализируется только для чтения. API локальных рекомендаций сохраняется для
|
||||||
|
совместимости и точечных сценариев, но реестр качества работает на уровне правил.
|
||||||
|
|
||||||
## Привязка преподаватель ↔ дисциплина
|
## Привязка преподаватель ↔ дисциплина
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ frontend/
|
|||||||
│ │ ├── schedule-view.js # Просмотр расписаний и запуск разовой правки из карточки
|
│ │ ├── schedule-view.js # Просмотр расписаний и запуск разовой правки из карточки
|
||||||
│ │ ├── schedule-override-panel.js # Боковая панель и реестр разовых изменений
|
│ │ ├── schedule-override-panel.js # Боковая панель и реестр разовых изменений
|
||||||
│ │ ├── teacher-absences.js # Отсутствия, согласование пожеланий и заявок на изменение
|
│ │ ├── teacher-absences.js # Отсутствия, согласование пожеланий и заявок на изменение
|
||||||
│ │ ├── schedule-quality.js # Оценка, фильтры и локальные рекомендации
|
│ │ ├── schedule-quality.js # Оценка, фильтры и переход к редактированию правил
|
||||||
│ │ ├── schedule-versions.js # Черновики, публикация, восстановление, diff и аудит
|
│ │ ├── schedule-versions.js # Черновики, публикация, восстановление, diff и аудит
|
||||||
│ │ ├── schedule.js # Конструктор правил и подсказки пожеланий преподавателей
|
│ │ ├── schedule.js # Конструктор правил и подсказки пожеланий преподавателей
|
||||||
│ │ ├── academic-calendar-grid.js # Расчёт ISO-недели дневной сетки
|
│ │ ├── academic-calendar-grid.js # Расчёт ISO-недели дневной сетки
|
||||||
@@ -183,7 +183,7 @@ frontend/
|
|||||||
| `teacher-absences` | Запросы преподавателей: отсутствия и мастер замены, согласование семестровых пожеланий, заявки на перенос, аудиторию или отмену | `/api/teacher-absences`, `/api/teacher-preferences`, `/api/teacher-change-requests`, `/api/users/teachers` |
|
| `teacher-absences` | Запросы преподавателей: отсутствия и мастер замены, согласование семестровых пожеланий, заявки на перенос, аудиторию или отмену | `/api/teacher-absences`, `/api/teacher-preferences`, `/api/teacher-change-requests`, `/api/users/teachers` |
|
||||||
| `schedule` | Конструктор правил: по умолчанию редактирует опубликованное расписание, позволяет переключиться на черновик или создать его из выбранной версии; содержит подсказки пожеланий и визуальную матрицу групп | `/api/admin/schedule-rules`, `/api/edu-office/schedule/versions`, `/api/teacher-preferences`, `/api/admin/time-slots`, `/api/admin/calendar/years`, `/api/lesson-types`, `/api/subgroups` |
|
| `schedule` | Конструктор правил: по умолчанию редактирует опубликованное расписание, позволяет переключиться на черновик или создать его из выбранной версии; содержит подсказки пожеланий и визуальную матрицу групп | `/api/admin/schedule-rules`, `/api/edu-office/schedule/versions`, `/api/teacher-preferences`, `/api/admin/time-slots`, `/api/admin/calendar/years`, `/api/lesson-types`, `/api/subgroups` |
|
||||||
| `schedule-versions` | Контур публикации: текущая версия, черновики, diff, архив, восстановление и журнал | `/api/edu-office/schedule/versions` |
|
| `schedule-versions` | Контур публикации: текущая версия, черновики, diff, архив, восстановление и журнал | `/api/edu-office/schedule/versions` |
|
||||||
| `schedule-quality` | Диагностика выбранной версии семестра: оценка, метрики, фильтруемые проблемы; для публикации — подтверждаемое локальное улучшение | `/api/edu-office/schedule/quality`, `/api/edu-office/schedule/quality/recommendations`, `/api/edu-office/schedule/overrides` |
|
| `schedule-quality` | Диагностика выбранной версии семестра: оценка, метрики и проблемные правила с переходом к их редактированию | `/api/edu-office/schedule/quality`, `/api/admin/schedule-rules` |
|
||||||
| `academic-calendar` | Учебные годы, семестры, создание календарных графиков, Excel-подобный редактор дневной сетки и привязка дисциплин к семестрам графика | `/api/admin/calendar`, `/api/admin/academic-calendars`, `/api/admin/academic-calendars/{id}/subjects`, `/api/admin/calendar/activity-types`, `/api/specialties`, `/api/specialties/{id}/profiles`, `/api/education-forms`, `/api/subjects` |
|
| `academic-calendar` | Учебные годы, семестры, создание календарных графиков, Excel-подобный редактор дневной сетки и привязка дисциплин к семестрам графика | `/api/admin/calendar`, `/api/admin/academic-calendars`, `/api/admin/academic-calendars/{id}/subjects`, `/api/admin/calendar/activity-types`, `/api/specialties`, `/api/specialties/{id}/profiles`, `/api/education-forms`, `/api/subjects` |
|
||||||
| `auditorium-workload` | Динамическая загруженность аудиторий, преподавателей и кафедр: сводная матрица по дате или совмещённая таблица выбранной сущности по чётной/нечётной неделе | `/api/classrooms`, `/api/users/teachers`, `/api/departments`, `/api/admin/time-slots`, `/api/equipments`, `/api/groups`, `/api/schedule`, `/api/admin/calendar/years` |
|
| `auditorium-workload` | Динамическая загруженность аудиторий, преподавателей и кафедр: сводная матрица по дате или совмещённая таблица выбранной сущности по чётной/нечётной неделе | `/api/classrooms`, `/api/users/teachers`, `/api/departments`, `/api/admin/time-slots`, `/api/equipments`, `/api/groups`, `/api/schedule`, `/api/admin/calendar/years` |
|
||||||
|
|
||||||
@@ -209,14 +209,13 @@ frontend/
|
|||||||
действий. Каждая карточка показывает исходное и запрошенное состояние, результат
|
действий. Каждая карточка показывает исходное и запрошенное состояние, результат
|
||||||
предварительной проверки и хронологию решения.
|
предварительной проверки и хронологию решения.
|
||||||
- Вкладка `schedule-quality` доступна администратору и учебному отделу. После выбора
|
- Вкладка `schedule-quality` доступна администратору и учебному отделу. После выбора
|
||||||
семестра и версии она выводит круговую оценку от 0 до 100, карточки метрик и реестр проблем с
|
семестра и версии она выводит круговую оценку от 0 до 100, карточки метрик и реестр правил
|
||||||
фильтрами по серьёзности и типу. Для проблемы, связанной с конкретным незакреплённым
|
с фильтрами по серьёзности и типу. Все проблемные занятия одного правила показаны одной
|
||||||
занятием опубликованной версии, кнопка `Подобрать улучшение` запрашивает проверенные варианты времени и
|
карточкой; в ней выводятся число затронутых занятий, категории и суммарный штраф. Кнопка
|
||||||
аудитории. Карточка кандидата показывает изменение оценки, улучшения и компромиссы;
|
`Изменить правило` открывает выбранную версию в конструкторе и сразу заполняет форму этим
|
||||||
override создаётся только после явного подтверждения, затем анализ запускается заново.
|
правилом. Сценарий одинаков для `PUBLISHED` и `DRAFT`; `ARCHIVED` анализируется только для
|
||||||
Для `DRAFT` экран рассчитывает те же метрики и проблемы напрямую по правилам черновика,
|
чтения. Переход между вкладками передаёт ID версии и правила через одноразовые ключи
|
||||||
но направляет пользователя в конструктор и не создаёт override. `ARCHIVED` анализируется
|
`magistr.schedule.openVersionId` и `magistr.schedule.openRuleId` в `localStorage`.
|
||||||
только для чтения. Экран не предлагает автоматически менять занятия, уже отредактированные вручную.
|
|
||||||
- Вкладка `schedule-versions` доступна администратору и учебному отделу и оформлена как
|
- Вкладка `schedule-versions` доступна администратору и учебному отделу и оформлена как
|
||||||
отдельный контур публикации. Верхняя карточка показывает версию, которую видят конечные
|
отдельный контур публикации. Верхняя карточка показывает версию, которую видят конечные
|
||||||
пользователи; ниже расположены черновики, сравнение правил и занятий, архив и журнал.
|
пользователи; ниже расположены черновики, сравнение правил и занятий, архив и журнал.
|
||||||
|
|||||||
@@ -503,6 +503,38 @@
|
|||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quality-rule-issues,
|
||||||
|
.quality-rule-detail-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-rule-issues > span,
|
||||||
|
.quality-rule-detail-list > span {
|
||||||
|
padding: 0.22rem 0.45rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: color-mix(in srgb, var(--quality-amber) 9%, transparent);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--quality-amber) 24%, var(--bg-card-border));
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.66rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-rule-detail-list {
|
||||||
|
display: grid;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-rule-detail-list > span {
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
font-size: 0.76rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-edit-rule {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.quality-penalty {
|
.quality-penalty {
|
||||||
color: var(--quality-red);
|
color: var(--quality-red);
|
||||||
font-family: Georgia, 'Times New Roman', serif;
|
font-family: Georgia, 'Times New Roman', serif;
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ function renderProblems() {
|
|||||||
`).join('')}</span>
|
`).join('')}</span>
|
||||||
<small>
|
<small>
|
||||||
Правило #${escapeHtml(String(problem.scheduleRuleId || '—'))}
|
Правило #${escapeHtml(String(problem.scheduleRuleId || '—'))}
|
||||||
· ${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))} снижают оценку
|
· затронуто ${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))}
|
||||||
· суммарный штраф
|
· суммарный штраф
|
||||||
</small>
|
</small>
|
||||||
</span>
|
</span>
|
||||||
@@ -238,7 +238,7 @@ function renderRuleDetails(problem) {
|
|||||||
<div class="quality-recommendations-head">
|
<div class="quality-recommendations-head">
|
||||||
<span class="quality-kicker">Правило #${escapeHtml(String(problem.scheduleRuleId || '—'))}</span>
|
<span class="quality-kicker">Правило #${escapeHtml(String(problem.scheduleRuleId || '—'))}</span>
|
||||||
<h3>${escapeHtml(problem.subjectName || problem.title)}</h3>
|
<h3>${escapeHtml(problem.subjectName || problem.title)}</h3>
|
||||||
<p>${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))} снижают оценку; суммарный штраф −${escapeHtml(String(problem.penalty))}.</p>
|
<p>Затронуто ${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))}; суммарный штраф −${escapeHtml(String(problem.penalty))}.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="quality-rule-detail-list">
|
<div class="quality-rule-detail-list">
|
||||||
${issueLabels.map(label => `<span>${escapeHtml(label)}</span>`).join('')}
|
${issueLabels.map(label => `<span>${escapeHtml(label)}</span>`).join('')}
|
||||||
|
|||||||
@@ -491,12 +491,25 @@ export async function initSchedule() {
|
|||||||
}
|
}
|
||||||
renderRules();
|
renderRules();
|
||||||
renderVisualSchedule();
|
renderVisualSchedule();
|
||||||
|
openRequestedRule();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
rulesTbody.innerHTML = `<tr><td colspan="7" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
|
rulesTbody.innerHTML = `<tr><td colspan="7" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
|
||||||
setVisualState('Ошибка загрузки правил расписания');
|
setVisualState('Ошибка загрузки правил расписания');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openRequestedRule() {
|
||||||
|
const requestedRuleId = localStorage.getItem('magistr.schedule.openRuleId');
|
||||||
|
if (!requestedRuleId) return;
|
||||||
|
localStorage.removeItem('magistr.schedule.openRuleId');
|
||||||
|
const rule = rules.find(item => String(item.id) === requestedRuleId);
|
||||||
|
if (!rule) {
|
||||||
|
showAlert('schedule-rule-alert', 'Не удалось найти выбранное правило в этом расписании.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fillRuleForm(rule);
|
||||||
|
}
|
||||||
|
|
||||||
function renderRules() {
|
function renderRules() {
|
||||||
if (!rules.length) {
|
if (!rules.length) {
|
||||||
rulesTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Правила расписания не созданы</td></tr>';
|
rulesTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Правила расписания не созданы</td></tr>';
|
||||||
@@ -2079,6 +2092,13 @@ export async function initSchedule() {
|
|||||||
return new Date(year, month - 1, day);
|
return new Date(year, month - 1, day);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function localIsoDate(date) {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
function compactWeekRanges(weeks, maxWeeks, parity) {
|
function compactWeekRanges(weeks, maxWeeks, parity) {
|
||||||
const uniqueWeeks = [...new Set((weeks || []).map(Number).filter(Boolean))].sort((a, b) => a - b);
|
const uniqueWeeks = [...new Set((weeks || []).map(Number).filter(Boolean))].sort((a, b) => a - b);
|
||||||
if (!uniqueWeeks.length) return '';
|
if (!uniqueWeeks.length) return '';
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<h2 id="quality-lab-title">Качество видно до перестановок</h2>
|
<h2 id="quality-lab-title">Качество видно до перестановок</h2>
|
||||||
<p>
|
<p>
|
||||||
Анализатор находит окна, перегруженные дни, неудачные аудитории и нарушения
|
Анализатор находит окна, перегруженные дни, неудачные аудитории и нарушения
|
||||||
пожеланий. Черновик можно оценить до публикации, не показывая промежуточное
|
пожеланий. Все проблемные занятия объединяются по правилу, а их штрафы
|
||||||
расписание студентам и преподавателям.
|
суммируются. Черновик можно оценить до публикации.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="quality-run-panel">
|
<div class="quality-run-panel">
|
||||||
@@ -55,8 +55,8 @@
|
|||||||
<div class="quality-problems-column">
|
<div class="quality-problems-column">
|
||||||
<div class="quality-section-heading">
|
<div class="quality-section-heading">
|
||||||
<div>
|
<div>
|
||||||
<span class="quality-kicker">Карта неудобств</span>
|
<span class="quality-kicker">Карта правил</span>
|
||||||
<h3>Что снижает оценку</h3>
|
<h3>Правила, снижающие оценку</h3>
|
||||||
</div>
|
</div>
|
||||||
<span class="quality-problem-total" id="quality-problem-total"></span>
|
<span class="quality-problem-total" id="quality-problem-total"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,8 +89,8 @@
|
|||||||
<aside class="quality-recommendations" id="quality-recommendations" aria-live="polite">
|
<aside class="quality-recommendations" id="quality-recommendations" aria-live="polite">
|
||||||
<div class="quality-recommendations-empty">
|
<div class="quality-recommendations-empty">
|
||||||
<span class="quality-recommendations-mark">↗</span>
|
<span class="quality-recommendations-mark">↗</span>
|
||||||
<h3>Локальные улучшения</h3>
|
<h3>Работа с правилом</h3>
|
||||||
<p>Выберите проблему, чтобы проверить свободные слоты и аудитории.</p>
|
<p>Выберите правило, чтобы увидеть суммарное влияние и открыть его в конструкторе.</p>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ test('оценка качества получает понятный диапа
|
|||||||
|
|
||||||
test('фильтр проблем одновременно учитывает важность и категорию', () => {
|
test('фильтр проблем одновременно учитывает важность и категорию', () => {
|
||||||
const problems = [
|
const problems = [
|
||||||
{ type: 'GROUP_GAP', severity: 'HIGH' },
|
{ type: 'GROUP_GAP', problemTypes: ['GROUP_GAP', 'ROOM_OVERSIZED'], severity: 'HIGH' },
|
||||||
{ type: 'ROOM_OVERSIZED', severity: 'LOW' },
|
{ type: 'ROOM_OVERSIZED', severity: 'LOW' },
|
||||||
{ type: 'TEACHER_SOFT_UNWANTED', severity: 'HIGH' }
|
{ type: 'TEACHER_SOFT_UNWANTED', severity: 'HIGH' }
|
||||||
];
|
];
|
||||||
@@ -28,7 +28,7 @@ test('фильтр проблем одновременно учитывает в
|
|||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
filterQualityProblems(problems, '', 'ROOM'),
|
filterQualityProblems(problems, '', 'ROOM'),
|
||||||
[problems[1]]
|
[problems[0], problems[1]]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -47,6 +47,8 @@ test('метрики и вкладка анализа подключены к SP
|
|||||||
assert.doesNotMatch(capabilities, /DEPARTMENT:[\s\S]*schedule-quality/);
|
assert.doesNotMatch(capabilities, /DEPARTMENT:[\s\S]*schedule-quality/);
|
||||||
assert.match(main, /'schedule-quality':[\s\S]*schedule-quality\.js/);
|
assert.match(main, /'schedule-quality':[\s\S]*schedule-quality\.js/);
|
||||||
assert.match(index, /data-tab="schedule-quality"/);
|
assert.match(index, /data-tab="schedule-quality"/);
|
||||||
assert.match(view, /Локальные улучшения/);
|
assert.match(view, /Правила, снижающие оценку/);
|
||||||
assert.match(script, /Применить после подтверждения/);
|
assert.match(script, /Изменить правило/);
|
||||||
|
assert.match(script, /magistr\.schedule\.openRuleId/);
|
||||||
|
assert.doesNotMatch(script, /quality\/recommendations/);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ test('контур версий подключён к SPA, а конструкт
|
|||||||
assert.match(constructor, /scheduleVersionId:\s*Number\(scheduleVersionSelect\.value\)/);
|
assert.match(constructor, /scheduleVersionId:\s*Number\(scheduleVersionSelect\.value\)/);
|
||||||
assert.match(constructor, /basedOnVersionId:\s*Number\(base\.id\)/);
|
assert.match(constructor, /basedOnVersionId:\s*Number\(base\.id\)/);
|
||||||
assert.match(constructor, /magistr\.schedule\.openVersionId/);
|
assert.match(constructor, /magistr\.schedule\.openVersionId/);
|
||||||
|
assert.match(constructor, /magistr\.schedule\.openRuleId/);
|
||||||
|
assert.match(constructor, /function localIsoDate\(date\)/);
|
||||||
assert.match(constructor, /data-slot-id/);
|
assert.match(constructor, /data-slot-id/);
|
||||||
assert.match(view, /id="schedule-version-create-draft"/);
|
assert.match(view, /id="schedule-version-create-draft"/);
|
||||||
assert.match(quality, /versionId=.*encodeURIComponent\(versionId\)/);
|
assert.match(quality, /versionId=.*encodeURIComponent\(versionId\)/);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
type: "query"
|
||||||
|
date: "2026-08-12T18:41:53.854324+00:00"
|
||||||
|
question: "в качестве расписания если правило снижает оценку качества расписания - то отображаются все пары которые это качество снижают, а должно отображаться одно правило, чтобы можно было поменять его, а не каждую пару по отдельности. баллы при этом должны суммироваться в зависимости от количества пар в правиле"
|
||||||
|
contributor: "graphify"
|
||||||
|
outcome: "dead_end"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Q: в качестве расписания если правило снижает оценку качества расписания - то отображаются все пары которые это качество снижают, а должно отображаться одно правило, чтобы можно было поменять его, а не каждую пару по отдельности. баллы при этом должны суммироваться в зависимости от количества пар в правиле
|
||||||
|
|
||||||
|
## Answer
|
||||||
|
|
||||||
|
Query expanded to (from graph vocab, 7 tokens): [schedule, rule, lesson, service, dto, controller, generator]. Graph traversal returned generic Service nodes about authentication and tenant infrastructure and did not identify the quality-analysis flow, so the result was not used as source evidence. The implementation was verified directly in ScheduleQualityService, ScheduleQualityAnalysisDto, schedule-quality.js and schedule.js.
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
- Signal: dead_end
|
||||||
Reference in New Issue
Block a user