This commit is contained in:
2026-09-02 23:47:00 +03:00
parent b4d0b0c8e9
commit a8830bcd91
77 changed files with 2998 additions and 0 deletions
@@ -0,0 +1,39 @@
package agzam.vyatsu.domain
import agzam.vyatsu.domain.model.DateRange
import agzam.vyatsu.domain.model.Season
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
object ScheduleDates {
private val zone: ZoneId = ZoneId.of("Europe/Moscow")
private val format: DateTimeFormatter = DateTimeFormatter.ofPattern("ddMMyyyy")
fun moscowToday(): LocalDate = LocalDate.now(zone)
fun asDate(dateStr: String): LocalDate = LocalDate.parse(dateStr, format)
fun dayIndexes(firstDate: String, today: LocalDate = moscowToday()): Pair<Int, Int> {
val begin = asDate(firstDate)
val anchor = begin.with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY))
val diff = ChronoUnit.DAYS.between(anchor, today)
var week = Math.floorMod(diff / 7, 2)
var day = Math.floorMod(diff, 7)
if (day == 6) {
day = (day + 1) % 7
week = (week + 1) % 2
}
return week to day
}
fun dateByIndexes(firstDate: String, week: Int, day: Int): LocalDate =
asDate(firstDate).plusDays((week * 7 + day).toLong())
fun currentSeason(today: LocalDate = moscowToday()): Season =
if (today.monthValue in 8..12) Season.AUTUMN else Season.SPRING
}
@@ -0,0 +1,6 @@
package agzam.vyatsu.domain.model
data class DateRange(
val first: String,
val second: String
)
@@ -0,0 +1,16 @@
package agzam.vyatsu.domain.model
import java.time.LocalDate
/**
* День
* @author Agzam
*/
data class Day(
/** Дата, парсится из формата dd.mm.yy из PDF **/
val date: LocalDate,
/** Список пар **/
val intervals: List<TimeSlot>,
)
@@ -0,0 +1,11 @@
package agzam.vyatsu.domain.model
/**
* Группа студентов ВятГУ.
* id — идентификатор группы из data-grp_period_id без последней цифры (как в PDF URL).
*/
data class Group(
val id: String,
val name: String,
val faculty: String
)
@@ -0,0 +1,29 @@
package agzam.vyatsu.domain.model
/**
* Занятие
* @author Agzam
*/
data class Lesson(
/** Сырое значение из PDF **/
val raw: String,
/** Кабинет **/
val room: String? = null,
/** Группа, например ИВТб-3304-05-00 **/
val group: String? = null,
/** Подгруппа, например на физкультуре **/
val subgroup: String? = null,
/** Предмет **/
val subject: String? = null,
/** Тип, например лекция/практика **/
val type: LessonType = LessonType.OTHER,
/** Преподаватель **/
val teacher: String? = null,
)
@@ -0,0 +1,18 @@
package agzam.vyatsu.domain.model
enum class LessonType(val label: String) {
LECTURE("Лекция"),
PRACTICE("Практическое занятие"),
LABORATORY("Лабораторная работа"),
OTHER("Занятие");
companion object {
fun fromRaw(raw: String): LessonType = when (raw.trim()) {
"Лекция" -> LECTURE
"Практическое занятие" -> PRACTICE
"Лабораторная работа" -> LABORATORY
else -> OTHER
}
}
}
@@ -0,0 +1,17 @@
package agzam.vyatsu.domain.model
/**
* Расписание группы, как правило содержит 2 недели
* @author Agzam
*/
data class Schedule(
val groupId: String,
val season: Season,
/** Может содержать более 7 дней */
val days: List<Day>
) {
}
@@ -0,0 +1,11 @@
package agzam.vyatsu.domain.model
enum class Season {
AUTUMN, SPRING;
val apiKey: String
get() = when (this) {
AUTUMN -> "1"
SPRING -> "2"
}
}
@@ -0,0 +1,17 @@
package agzam.vyatsu.domain.model
import java.time.LocalTime
/**
* Временной промежуток/пара (например 08:20-09:30 из PDF)
*
* Может содержать несколько [Lesson], например для разделения на подгруппы
* @author Agzam
*/
data class TimeSlot(
val start: LocalTime,
val end: LocalTime,
val lessons: List<Lesson>
)
@@ -0,0 +1,12 @@
package agzam.vyatsu.domain.repository
import agzam.vyatsu.domain.model.Group
import kotlinx.coroutines.flow.Flow
interface GroupRepository {
fun observeGroups(): Flow<List<Group>>
suspend fun getGroups(): List<Group>
suspend fun getFaculties(): List<String>
suspend fun refreshIfStale(): Boolean
suspend fun forceRefresh(): Boolean
}
@@ -0,0 +1,7 @@
package agzam.vyatsu.domain.repository
interface PreferencesRepository {
suspend fun selectedGroupId(): String?
suspend fun setSelectedGroup(groupId: String)
suspend fun clearSelectedGroup()
}
@@ -0,0 +1,12 @@
package agzam.vyatsu.domain.repository
import agzam.vyatsu.domain.model.Schedule
import kotlinx.coroutines.flow.Flow
interface ScheduleRepository {
fun observeSchedule(groupId: String): Flow<Schedule?>
suspend fun getSchedule(groupId: String): Schedule?
suspend fun sync(groupId: String): Schedule?
suspend fun isStale(groupId: String): Boolean
suspend fun clear(groupId: String)
}