feat: department display
This commit is contained in:
@@ -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?
|
||||||
|
)
|
||||||
@@ -61,13 +61,15 @@ interface TeacherScheduleDao {
|
|||||||
fun observeDaysForPeriod(season: String, weekStart: Long): Flow<List<TeacherDayEntity>>
|
fun observeDaysForPeriod(season: String, weekStart: Long): Flow<List<TeacherDayEntity>>
|
||||||
|
|
||||||
@Query(
|
@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 " +
|
"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 " +
|
"WHERE teacher_schedules.season = :season AND teacher_schedules.weekStart <= :weekStart " +
|
||||||
"AND teacher_schedules.weekEnd >= :weekStart " +
|
"AND teacher_schedules.weekEnd >= :weekStart " +
|
||||||
"ORDER BY teacher_lessons.date, teacher_lessons.slotIndex, teacher_lessons.lessonIndex"
|
"ORDER BY teacher_lessons.date, teacher_lessons.slotIndex, teacher_lessons.lessonIndex"
|
||||||
)
|
)
|
||||||
fun observeLessonsForPeriod(season: String, weekStart: Long): Flow<List<TeacherLessonEntity>>
|
fun observeLessonsForPeriod(season: String, weekStart: Long): Flow<List<TeacherLessonWithDepartment>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT DISTINCT teacher FROM teacher_lessons INNER JOIN teacher_days ON teacher_days.id = teacher_lessons.dayId " +
|
"SELECT DISTINCT teacher FROM teacher_lessons INNER JOIN teacher_days ON teacher_days.id = teacher_lessons.dayId " +
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import agzam.vyatsu.data.local.DepartmentPeriodEntity
|
|||||||
import agzam.vyatsu.data.local.TeacherDao
|
import agzam.vyatsu.data.local.TeacherDao
|
||||||
import agzam.vyatsu.data.local.TeacherDayEntity
|
import agzam.vyatsu.data.local.TeacherDayEntity
|
||||||
import agzam.vyatsu.data.local.TeacherLessonEntity
|
import agzam.vyatsu.data.local.TeacherLessonEntity
|
||||||
|
import agzam.vyatsu.data.local.TeacherLessonWithDepartment
|
||||||
import agzam.vyatsu.data.local.TeacherScheduleDao
|
import agzam.vyatsu.data.local.TeacherScheduleDao
|
||||||
import agzam.vyatsu.data.local.TeacherScheduleEntity
|
import agzam.vyatsu.data.local.TeacherScheduleEntity
|
||||||
import agzam.vyatsu.data.remote.TeacherScheduleParser
|
import agzam.vyatsu.data.remote.TeacherScheduleParser
|
||||||
@@ -238,30 +239,30 @@ class TeacherScheduleRepositoryImpl(
|
|||||||
private fun buildTeacherSchedule(
|
private fun buildTeacherSchedule(
|
||||||
period: SchedulePeriod,
|
period: SchedulePeriod,
|
||||||
dayEntities: List<TeacherDayEntity>,
|
dayEntities: List<TeacherDayEntity>,
|
||||||
lessonEntities: List<TeacherLessonEntity>
|
lessons: List<TeacherLessonWithDepartment>
|
||||||
): TeacherSchedule? {
|
): TeacherSchedule? {
|
||||||
val weekDays = dayEntities
|
val weekDays = dayEntities
|
||||||
.filter {
|
.filter {
|
||||||
it.date >= period.weekStart.toEpochDay() && it.date <= period.weekEnd.toEpochDay()
|
it.date >= period.weekStart.toEpochDay() && it.date <= period.weekEnd.toEpochDay()
|
||||||
}
|
}
|
||||||
if (weekDays.isEmpty()) return null
|
if (weekDays.isEmpty()) return null
|
||||||
val lessonsByDate = lessonEntities.groupBy { it.date }
|
val lessonsByDate = lessons.groupBy { it.lesson.date }
|
||||||
val days = weekDays
|
val days = weekDays
|
||||||
.sortedBy { it.date }
|
.sortedBy { it.date }
|
||||||
.distinctBy { it.date }
|
.distinctBy { it.date }
|
||||||
.map { day ->
|
.map { day ->
|
||||||
val lessons = lessonsByDate[day.date].orEmpty()
|
val dayLessons = lessonsByDate[day.date].orEmpty()
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"build: date=${day.date} totalLessons=${lessons.size} " +
|
"build: date=${day.date} totalLessons=${dayLessons.size} " +
|
||||||
"groupedByStart=${lessons.groupBy { it.start }.keys.sorted()}"
|
"groupedByStart=${dayLessons.groupBy { it.lesson.start }.keys.sorted()}"
|
||||||
)
|
)
|
||||||
val slots = lessons.groupBy { it.start }
|
val slots = dayLessons.groupBy { it.lesson.start }
|
||||||
.toSortedMap()
|
.toSortedMap()
|
||||||
.map { (startMin, slotLessons) ->
|
.map { (startMin, slotLessons) ->
|
||||||
val first = slotLessons.first()
|
val first = slotLessons.first().lesson
|
||||||
val times = slotLessons.map { "${it.start}-${it.end}" }.distinct()
|
val times = slotLessons.map { "${it.lesson.start}-${it.lesson.end}" }.distinct()
|
||||||
val teachers = slotLessons.map { it.teacher }.distinct()
|
val teachers = slotLessons.map { it.lesson.teacher }.distinct()
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"build: date=${day.date} start=$startMin firstEnd=$first.end " +
|
"build: date=${day.date} start=$startMin firstEnd=$first.end " +
|
||||||
@@ -271,7 +272,7 @@ class TeacherScheduleRepositoryImpl(
|
|||||||
start = LocalTime.of(startMin / 60, startMin % 60),
|
start = LocalTime.of(startMin / 60, startMin % 60),
|
||||||
end = LocalTime.of(first.end / 60, first.end % 60),
|
end = LocalTime.of(first.end / 60, first.end % 60),
|
||||||
lessons = slotLessons
|
lessons = slotLessons
|
||||||
.sortedBy { it.lessonIndex }
|
.sortedBy { it.lesson.lessonIndex }
|
||||||
.map { it.toDomain() }
|
.map { it.toDomain() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -280,16 +281,33 @@ class TeacherScheduleRepositoryImpl(
|
|||||||
return TeacherSchedule(period = period, days = days)
|
return TeacherSchedule(period = period, days = days)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TeacherLessonEntity.toDomain(): Lesson = Lesson(
|
private fun TeacherLessonWithDepartment.toDomain(): Lesson = Lesson(
|
||||||
raw = raw,
|
raw = lesson.raw,
|
||||||
room = room,
|
room = lesson.room,
|
||||||
group = groupName,
|
group = lesson.groupName,
|
||||||
subgroup = subgroup,
|
subgroup = lesson.subgroup,
|
||||||
subject = subject,
|
subject = lesson.subject,
|
||||||
type = type,
|
type = lesson.type,
|
||||||
teacher = teacher
|
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 LocalTime.toMinutesOfDay(): Int = hour * 60 + minute
|
||||||
|
|
||||||
private fun now(): Long = System.currentTimeMillis()
|
private fun now(): Long = System.currentTimeMillis()
|
||||||
|
|||||||
@@ -26,4 +26,6 @@ data class Lesson(
|
|||||||
/** Преподаватель **/
|
/** Преподаватель **/
|
||||||
val teacher: String? = null,
|
val teacher: String? = null,
|
||||||
|
|
||||||
|
/** Кафедра-источник (только в расписании преподавателей) **/
|
||||||
|
val department: String? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -70,6 +70,16 @@ internal fun LessonSlot(slotIndex: Int, slot: TimeSlot, showTeacher: Boolean = t
|
|||||||
) {
|
) {
|
||||||
var any = false
|
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(
|
val meta = listOfNotNull(
|
||||||
if (lesson.type == LessonType.OTHER) {
|
if (lesson.type == LessonType.OTHER) {
|
||||||
null
|
null
|
||||||
|
|||||||
Reference in New Issue
Block a user