From 57e5bd9af8c32a32676c3942566e529bddb6d706 Mon Sep 17 00:00:00 2001 From: Agzamus Date: Thu, 8 Oct 2026 02:10:26 +0300 Subject: [PATCH] feat: department display --- .../data/local/TeacherLessonWithDepartment.kt | 10 ++++ .../vyatsu/data/local/TeacherScheduleDao.kt | 6 ++- .../TeacherScheduleRepositoryImpl.kt | 54 ++++++++++++------- .../java/agzam/vyatsu/domain/model/Lesson.kt | 2 + .../ui/schedule/components/LessonSlot.kt | 10 ++++ 5 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/agzam/vyatsu/data/local/TeacherLessonWithDepartment.kt diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherLessonWithDepartment.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherLessonWithDepartment.kt new file mode 100644 index 0000000..31f6b63 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherLessonWithDepartment.kt @@ -0,0 +1,10 @@ +package agzam.vyatsu.data.local + +import androidx.room.Embedded + +/** Занятие преподавателя + кафедра-источник. Проекция LEFT JOIN, схема Room не меняется. */ +data class TeacherLessonWithDepartment( + @Embedded val lesson: TeacherLessonEntity, + val departmentId: String, + val departmentName: String? +) \ No newline at end of file diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt index 57b0684..f16034c 100644 --- a/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt @@ -61,13 +61,15 @@ interface TeacherScheduleDao { fun observeDaysForPeriod(season: String, weekStart: Long): Flow> @Query( - "SELECT teacher_lessons.* FROM teacher_lessons INNER JOIN teacher_days ON teacher_days.id = teacher_lessons.dayId " + + "SELECT teacher_lessons.*, teacher_schedules.departmentId AS departmentId, departments.name AS departmentName " + + "FROM teacher_lessons INNER JOIN teacher_days ON teacher_days.id = teacher_lessons.dayId " + "INNER JOIN teacher_schedules ON teacher_schedules.id = teacher_days.scheduleId " + + "LEFT JOIN departments ON departments.departmentId = teacher_schedules.departmentId " + "WHERE teacher_schedules.season = :season AND teacher_schedules.weekStart <= :weekStart " + "AND teacher_schedules.weekEnd >= :weekStart " + "ORDER BY teacher_lessons.date, teacher_lessons.slotIndex, teacher_lessons.lessonIndex" ) - fun observeLessonsForPeriod(season: String, weekStart: Long): Flow> + fun observeLessonsForPeriod(season: String, weekStart: Long): Flow> @Query( "SELECT DISTINCT teacher FROM teacher_lessons INNER JOIN teacher_days ON teacher_days.id = teacher_lessons.dayId " + diff --git a/app/src/main/java/agzam/vyatsu/data/repository/TeacherScheduleRepositoryImpl.kt b/app/src/main/java/agzam/vyatsu/data/repository/TeacherScheduleRepositoryImpl.kt index af52956..0a232df 100644 --- a/app/src/main/java/agzam/vyatsu/data/repository/TeacherScheduleRepositoryImpl.kt +++ b/app/src/main/java/agzam/vyatsu/data/repository/TeacherScheduleRepositoryImpl.kt @@ -4,6 +4,7 @@ import agzam.vyatsu.data.local.DepartmentPeriodEntity import agzam.vyatsu.data.local.TeacherDao import agzam.vyatsu.data.local.TeacherDayEntity import agzam.vyatsu.data.local.TeacherLessonEntity +import agzam.vyatsu.data.local.TeacherLessonWithDepartment import agzam.vyatsu.data.local.TeacherScheduleDao import agzam.vyatsu.data.local.TeacherScheduleEntity import agzam.vyatsu.data.remote.TeacherScheduleParser @@ -238,30 +239,30 @@ class TeacherScheduleRepositoryImpl( private fun buildTeacherSchedule( period: SchedulePeriod, dayEntities: List, - lessonEntities: List + lessons: List ): TeacherSchedule? { val weekDays = dayEntities .filter { it.date >= period.weekStart.toEpochDay() && it.date <= period.weekEnd.toEpochDay() } if (weekDays.isEmpty()) return null - val lessonsByDate = lessonEntities.groupBy { it.date } + val lessonsByDate = lessons.groupBy { it.lesson.date } val days = weekDays .sortedBy { it.date } .distinctBy { it.date } .map { day -> - val lessons = lessonsByDate[day.date].orEmpty() + val dayLessons = lessonsByDate[day.date].orEmpty() Log.d( TAG, - "build: date=${day.date} totalLessons=${lessons.size} " + - "groupedByStart=${lessons.groupBy { it.start }.keys.sorted()}" + "build: date=${day.date} totalLessons=${dayLessons.size} " + + "groupedByStart=${dayLessons.groupBy { it.lesson.start }.keys.sorted()}" ) - val slots = lessons.groupBy { it.start } + val slots = dayLessons.groupBy { it.lesson.start } .toSortedMap() .map { (startMin, slotLessons) -> - val first = slotLessons.first() - val times = slotLessons.map { "${it.start}-${it.end}" }.distinct() - val teachers = slotLessons.map { it.teacher }.distinct() + val first = slotLessons.first().lesson + val times = slotLessons.map { "${it.lesson.start}-${it.lesson.end}" }.distinct() + val teachers = slotLessons.map { it.lesson.teacher }.distinct() Log.d( TAG, "build: date=${day.date} start=$startMin firstEnd=$first.end " + @@ -271,7 +272,7 @@ class TeacherScheduleRepositoryImpl( start = LocalTime.of(startMin / 60, startMin % 60), end = LocalTime.of(first.end / 60, first.end % 60), lessons = slotLessons - .sortedBy { it.lessonIndex } + .sortedBy { it.lesson.lessonIndex } .map { it.toDomain() } ) } @@ -280,16 +281,33 @@ class TeacherScheduleRepositoryImpl( return TeacherSchedule(period = period, days = days) } - private fun TeacherLessonEntity.toDomain(): Lesson = Lesson( - raw = raw, - room = room, - group = groupName, - subgroup = subgroup, - subject = subject, - type = type, - teacher = teacher + private fun TeacherLessonWithDepartment.toDomain(): Lesson = Lesson( + raw = lesson.raw, + room = lesson.room, + group = lesson.groupName, + subgroup = lesson.subgroup, + subject = lesson.subject, + type = lesson.type, + teacher = lesson.teacher, + department = departmentLabel(departmentId, departmentName) ) + // Названия, не выводимые акронимом из слов (например «топ-уровня в сфере…») + private val DEPARTMENT_LABELS = mapOf( + "974" to "Топ ИИ" + ) + + private fun departmentLabel(departmentId: String, departmentName: String?): String { + DEPARTMENT_LABELS[departmentId]?.let { return it } + val name = departmentName ?: return "каф. $departmentId" + val words = name.removePrefix("Кафедра ").removePrefix("кафедра ") + .substringBefore(" (") + .split(" ") + .filter { it.isNotBlank() && it != "и" } + val acronym = words.joinToString("") { it.first().uppercase().toString() } + return acronym.ifBlank { "каф. $departmentId" } + } + private fun LocalTime.toMinutesOfDay(): Int = hour * 60 + minute private fun now(): Long = System.currentTimeMillis() diff --git a/app/src/main/java/agzam/vyatsu/domain/model/Lesson.kt b/app/src/main/java/agzam/vyatsu/domain/model/Lesson.kt index 7060241..7cf945b 100644 --- a/app/src/main/java/agzam/vyatsu/domain/model/Lesson.kt +++ b/app/src/main/java/agzam/vyatsu/domain/model/Lesson.kt @@ -26,4 +26,6 @@ data class Lesson( /** Преподаватель **/ val teacher: String? = null, + /** Кафедра-источник (только в расписании преподавателей) **/ + val department: String? = null, ) diff --git a/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt b/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt index 9ed0cbc..62fa24d 100644 --- a/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt +++ b/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt @@ -70,6 +70,16 @@ internal fun LessonSlot(slotIndex: Int, slot: TimeSlot, showTeacher: Boolean = t ) { var any = false + if (lesson.department != null) { + Text( + lesson.department, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(horizontal = hPadding) + ) + any = true + } + val meta = listOfNotNull( if (lesson.type == LessonType.OTHER) { null