diff --git a/app/schemas/agzam.vyatsu.data.local.AppDatabase/4.json b/app/schemas/agzam.vyatsu.data.local.AppDatabase/4.json new file mode 100644 index 0000000..b443070 --- /dev/null +++ b/app/schemas/agzam.vyatsu.data.local.AppDatabase/4.json @@ -0,0 +1,818 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "38abab3f8c5b39625b6a4de9d58968ca", + "entities": [ + { + "tableName": "groups", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `faculty` TEXT NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "faculty", + "columnName": "faculty", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "group_periods", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` TEXT NOT NULL, `season` TEXT NOT NULL, `weekStart` INTEGER NOT NULL, `weekEnd` INTEGER NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `groups`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "season", + "columnName": "season", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weekStart", + "columnName": "weekStart", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weekEnd", + "columnName": "weekEnd", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_periods_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_periods_groupId` ON `${TABLE_NAME}` (`groupId`)" + } + ], + "foreignKeys": [ + { + "table": "groups", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "schedules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` TEXT NOT NULL, `season` TEXT NOT NULL, `weekStart` INTEGER NOT NULL, `weekEnd` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `groups`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "season", + "columnName": "season", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weekStart", + "columnName": "weekStart", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weekEnd", + "columnName": "weekEnd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_schedules_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_schedules_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_schedules_groupId_season_weekStart", + "unique": true, + "columnNames": [ + "groupId", + "season", + "weekStart" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_schedules_groupId_season_weekStart` ON `${TABLE_NAME}` (`groupId`, `season`, `weekStart`)" + } + ], + "foreignKeys": [ + { + "table": "groups", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "days", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scheduleId` INTEGER NOT NULL, `date` INTEGER NOT NULL, FOREIGN KEY(`scheduleId`) REFERENCES `schedules`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scheduleId", + "columnName": "scheduleId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_days_scheduleId", + "unique": false, + "columnNames": [ + "scheduleId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_days_scheduleId` ON `${TABLE_NAME}` (`scheduleId`)" + }, + { + "name": "index_days_date", + "unique": false, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_days_date` ON `${TABLE_NAME}` (`date`)" + } + ], + "foreignKeys": [ + { + "table": "schedules", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "scheduleId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "lessons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dayId` INTEGER NOT NULL, `date` INTEGER NOT NULL, `slotIndex` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `lessonIndex` INTEGER NOT NULL, `raw` TEXT NOT NULL, `room` TEXT, `groupName` TEXT, `subgroup` TEXT, `subject` TEXT, `type` TEXT NOT NULL, `teacher` TEXT, FOREIGN KEY(`dayId`) REFERENCES `days`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dayId", + "columnName": "dayId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "slotIndex", + "columnName": "slotIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lessonIndex", + "columnName": "lessonIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "raw", + "columnName": "raw", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT" + }, + { + "fieldPath": "groupName", + "columnName": "groupName", + "affinity": "TEXT" + }, + { + "fieldPath": "subgroup", + "columnName": "subgroup", + "affinity": "TEXT" + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_lessons_dayId", + "unique": false, + "columnNames": [ + "dayId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_lessons_dayId` ON `${TABLE_NAME}` (`dayId`)" + }, + { + "name": "index_lessons_date", + "unique": false, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_lessons_date` ON `${TABLE_NAME}` (`date`)" + }, + { + "name": "index_lessons_subject", + "unique": false, + "columnNames": [ + "subject" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_lessons_subject` ON `${TABLE_NAME}` (`subject`)" + }, + { + "name": "index_lessons_room", + "unique": false, + "columnNames": [ + "room" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_lessons_room` ON `${TABLE_NAME}` (`room`)" + } + ], + "foreignKeys": [ + { + "table": "days", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dayId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "preferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + }, + { + "tableName": "departments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`departmentId` TEXT NOT NULL, `name` TEXT NOT NULL, `faculty` TEXT NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`departmentId`))", + "fields": [ + { + "fieldPath": "departmentId", + "columnName": "departmentId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "faculty", + "columnName": "faculty", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "departmentId" + ] + } + }, + { + "tableName": "department_periods", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `departmentId` TEXT NOT NULL, `season` TEXT NOT NULL, `weekStart` INTEGER NOT NULL, `weekEnd` INTEGER NOT NULL, FOREIGN KEY(`departmentId`) REFERENCES `departments`(`departmentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "departmentId", + "columnName": "departmentId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "season", + "columnName": "season", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weekStart", + "columnName": "weekStart", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weekEnd", + "columnName": "weekEnd", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_department_periods_departmentId", + "unique": false, + "columnNames": [ + "departmentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_department_periods_departmentId` ON `${TABLE_NAME}` (`departmentId`)" + } + ], + "foreignKeys": [ + { + "table": "departments", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "departmentId" + ], + "referencedColumns": [ + "departmentId" + ] + } + ] + }, + { + "tableName": "teacher_schedules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `departmentId` TEXT NOT NULL, `season` TEXT NOT NULL, `weekStart` INTEGER NOT NULL, `weekEnd` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, FOREIGN KEY(`departmentId`) REFERENCES `departments`(`departmentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "departmentId", + "columnName": "departmentId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "season", + "columnName": "season", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weekStart", + "columnName": "weekStart", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weekEnd", + "columnName": "weekEnd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_teacher_schedules_departmentId", + "unique": false, + "columnNames": [ + "departmentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_schedules_departmentId` ON `${TABLE_NAME}` (`departmentId`)" + }, + { + "name": "index_teacher_schedules_departmentId_season_weekStart", + "unique": true, + "columnNames": [ + "departmentId", + "season", + "weekStart" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_teacher_schedules_departmentId_season_weekStart` ON `${TABLE_NAME}` (`departmentId`, `season`, `weekStart`)" + } + ], + "foreignKeys": [ + { + "table": "departments", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "departmentId" + ], + "referencedColumns": [ + "departmentId" + ] + } + ] + }, + { + "tableName": "teacher_days", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scheduleId` INTEGER NOT NULL, `date` INTEGER NOT NULL, FOREIGN KEY(`scheduleId`) REFERENCES `teacher_schedules`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scheduleId", + "columnName": "scheduleId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_teacher_days_scheduleId", + "unique": false, + "columnNames": [ + "scheduleId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_days_scheduleId` ON `${TABLE_NAME}` (`scheduleId`)" + }, + { + "name": "index_teacher_days_date", + "unique": false, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_days_date` ON `${TABLE_NAME}` (`date`)" + } + ], + "foreignKeys": [ + { + "table": "teacher_schedules", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "scheduleId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "teacher_lessons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dayId` INTEGER NOT NULL, `date` INTEGER NOT NULL, `slotIndex` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `lessonIndex` INTEGER NOT NULL, `raw` TEXT NOT NULL, `room` TEXT, `groupName` TEXT, `subgroup` TEXT, `subject` TEXT, `type` TEXT NOT NULL, `teacher` TEXT NOT NULL, FOREIGN KEY(`dayId`) REFERENCES `teacher_days`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dayId", + "columnName": "dayId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "slotIndex", + "columnName": "slotIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lessonIndex", + "columnName": "lessonIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "raw", + "columnName": "raw", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT" + }, + { + "fieldPath": "groupName", + "columnName": "groupName", + "affinity": "TEXT" + }, + { + "fieldPath": "subgroup", + "columnName": "subgroup", + "affinity": "TEXT" + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_teacher_lessons_dayId", + "unique": false, + "columnNames": [ + "dayId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_dayId` ON `${TABLE_NAME}` (`dayId`)" + }, + { + "name": "index_teacher_lessons_teacher", + "unique": false, + "columnNames": [ + "teacher" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_teacher` ON `${TABLE_NAME}` (`teacher`)" + }, + { + "name": "index_teacher_lessons_room", + "unique": false, + "columnNames": [ + "room" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_room` ON `${TABLE_NAME}` (`room`)" + }, + { + "name": "index_teacher_lessons_subject", + "unique": false, + "columnNames": [ + "subject" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_subject` ON `${TABLE_NAME}` (`subject`)" + } + ], + "foreignKeys": [ + { + "table": "teacher_days", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dayId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '38abab3f8c5b39625b6a4de9d58968ca')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/agzam/vyatsu/data/local/AppDatabase.kt b/app/src/main/java/agzam/vyatsu/data/local/AppDatabase.kt index 76a8b7e..89f72df 100644 --- a/app/src/main/java/agzam/vyatsu/data/local/AppDatabase.kt +++ b/app/src/main/java/agzam/vyatsu/data/local/AppDatabase.kt @@ -3,6 +3,8 @@ package agzam.vyatsu.data.local import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase @Database( entities = [ @@ -11,9 +13,14 @@ import androidx.room.TypeConverters ScheduleEntity::class, DayEntity::class, LessonEntity::class, - PreferenceEntity::class + PreferenceEntity::class, + DepartmentEntity::class, + DepartmentPeriodEntity::class, + TeacherScheduleEntity::class, + TeacherDayEntity::class, + TeacherLessonEntity::class ], - version = 3, + version = 4, exportSchema = true ) @TypeConverters(Converters::class) @@ -21,4 +28,91 @@ abstract class AppDatabase : RoomDatabase() { abstract fun groupDao(): GroupDao abstract fun scheduleDao(): ScheduleDao abstract fun preferencesDao(): PreferencesDao + abstract fun teacherDao(): TeacherDao + abstract fun teacherScheduleDao(): TeacherScheduleDao + + companion object { + val MIGRATION_3_4 = object : Migration(3, 4) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( + "CREATE TABLE IF NOT EXISTS `departments` (" + + "`departmentId` TEXT NOT NULL, `name` TEXT NOT NULL, " + + "`faculty` TEXT NOT NULL, `lastSyncedAt` INTEGER NOT NULL, " + + "PRIMARY KEY(`departmentId`))" + ) + db.execSQL( + "CREATE TABLE IF NOT EXISTS `department_periods` (" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`departmentId` TEXT NOT NULL, `season` TEXT NOT NULL, " + + "`weekStart` INTEGER NOT NULL, `weekEnd` INTEGER NOT NULL, " + + "FOREIGN KEY(`departmentId`) REFERENCES `departments`(`departmentId`) " + + "ON UPDATE NO ACTION ON DELETE CASCADE )" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_department_periods_departmentId` " + + "ON `department_periods` (`departmentId`)" + ) + db.execSQL( + "CREATE TABLE IF NOT EXISTS `teacher_schedules` (" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`departmentId` TEXT NOT NULL, `season` TEXT NOT NULL, " + + "`weekStart` INTEGER NOT NULL, `weekEnd` INTEGER NOT NULL, " + + "`lastSyncedAt` INTEGER NOT NULL, " + + "FOREIGN KEY(`departmentId`) REFERENCES `departments`(`departmentId`) " + + "ON UPDATE NO ACTION ON DELETE CASCADE )" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_schedules_departmentId` " + + "ON `teacher_schedules` (`departmentId`)" + ) + db.execSQL( + "CREATE UNIQUE INDEX IF NOT EXISTS `index_teacher_schedules_departmentId_season_weekStart` " + + "ON `teacher_schedules` (`departmentId`, `season`, `weekStart`)" + ) + db.execSQL( + "CREATE TABLE IF NOT EXISTS `teacher_days` (" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`scheduleId` INTEGER NOT NULL, `date` INTEGER NOT NULL, " + + "FOREIGN KEY(`scheduleId`) REFERENCES `teacher_schedules`(`id`) " + + "ON UPDATE NO ACTION ON DELETE CASCADE )" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_days_scheduleId` " + + "ON `teacher_days` (`scheduleId`)" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_days_date` " + + "ON `teacher_days` (`date`)" + ) + db.execSQL( + "CREATE TABLE IF NOT EXISTS `teacher_lessons` (" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`dayId` INTEGER NOT NULL, `date` INTEGER NOT NULL, " + + "`slotIndex` INTEGER NOT NULL, `start` INTEGER NOT NULL, " + + "`end` INTEGER NOT NULL, `lessonIndex` INTEGER NOT NULL, " + + "`raw` TEXT NOT NULL, `room` TEXT, `groupName` TEXT, " + + "`subgroup` TEXT, `subject` TEXT, `type` TEXT NOT NULL, " + + "`teacher` TEXT NOT NULL, " + + "FOREIGN KEY(`dayId`) REFERENCES `teacher_days`(`id`) " + + "ON UPDATE NO ACTION ON DELETE CASCADE )" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_dayId` " + + "ON `teacher_lessons` (`dayId`)" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_teacher` " + + "ON `teacher_lessons` (`teacher`)" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_room` " + + "ON `teacher_lessons` (`room`)" + ) + db.execSQL( + "CREATE INDEX IF NOT EXISTS `index_teacher_lessons_subject` " + + "ON `teacher_lessons` (`subject`)" + ) + } + } + } } diff --git a/app/src/main/java/agzam/vyatsu/data/local/DepartmentEntity.kt b/app/src/main/java/agzam/vyatsu/data/local/DepartmentEntity.kt new file mode 100644 index 0000000..e81221b --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/DepartmentEntity.kt @@ -0,0 +1,12 @@ +package agzam.vyatsu.data.local + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "departments") +data class DepartmentEntity( + @PrimaryKey val departmentId: String, + val name: String, + val faculty: String, + val lastSyncedAt: Long +) diff --git a/app/src/main/java/agzam/vyatsu/data/local/DepartmentPeriodEntity.kt b/app/src/main/java/agzam/vyatsu/data/local/DepartmentPeriodEntity.kt new file mode 100644 index 0000000..190a9b3 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/DepartmentPeriodEntity.kt @@ -0,0 +1,28 @@ +package agzam.vyatsu.data.local + +import agzam.vyatsu.domain.model.Season +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +// Все опубликованные на сайте 2-недельные периоды кафедры, включая ещё не начавшиеся. +@Entity( + tableName = "department_periods", + foreignKeys = [ + ForeignKey( + entity = DepartmentEntity::class, + parentColumns = ["departmentId"], + childColumns = ["departmentId"], + onDelete = ForeignKey.CASCADE + ) + ], + indices = [Index("departmentId")] +) +data class DepartmentPeriodEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val departmentId: String, + val season: Season, + val weekStart: Long, + val weekEnd: Long +) diff --git a/app/src/main/java/agzam/vyatsu/data/local/PreferencesDao.kt b/app/src/main/java/agzam/vyatsu/data/local/PreferencesDao.kt index 215baaa..3687422 100644 --- a/app/src/main/java/agzam/vyatsu/data/local/PreferencesDao.kt +++ b/app/src/main/java/agzam/vyatsu/data/local/PreferencesDao.kt @@ -28,6 +28,36 @@ interface PreferencesDao { @Query("INSERT OR REPLACE INTO preferences(key, value) VALUES('selectedDayDate', :date)") suspend fun setSelectedDayDate(date: String) + @Query("SELECT value FROM preferences WHERE key = 'selectedTeacherId'") + suspend fun selectedTeacherId(): String? + + @Query( + "INSERT OR REPLACE INTO preferences(key, value) VALUES('selectedTeacherId', :teacherId)" + ) + suspend fun setSelectedTeacherId(teacherId: String) + + @Query("SELECT value FROM preferences WHERE key = 'selectedTeacherWeekStart'") + suspend fun selectedTeacherWeekStart(): String? + + @Query( + "INSERT OR REPLACE INTO preferences(key, value) VALUES('selectedTeacherWeekStart', :weekStart)" + ) + suspend fun setSelectedTeacherWeekStart(weekStart: String) + + @Query("SELECT value FROM preferences WHERE key = 'selectedTeacherDayDate'") + suspend fun selectedTeacherDayDate(): String? + + @Query( + "INSERT OR REPLACE INTO preferences(key, value) VALUES('selectedTeacherDayDate', :date)" + ) + suspend fun setSelectedTeacherDayDate(date: String) + + @Query( + "DELETE FROM preferences WHERE key IN " + + "('selectedTeacherId', 'selectedTeacherWeekStart', 'selectedTeacherDayDate')" + ) + suspend fun clearSelectedTeacher() + @Query("SELECT value FROM preferences WHERE key = 'autoUpdateEnabled'") suspend fun autoUpdateEnabled(): String? diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherDao.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherDao.kt new file mode 100644 index 0000000..c92486a --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherDao.kt @@ -0,0 +1,39 @@ +package agzam.vyatsu.data.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import kotlinx.coroutines.flow.Flow + +@Dao +interface TeacherDao { + @Query("SELECT * FROM departments ORDER BY name") + fun observeDepartments(): Flow> + + @Query("SELECT * FROM departments WHERE departmentId = :id") + suspend fun getDepartment(id: String): DepartmentEntity? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(departments: List) + + @Query("SELECT MAX(lastSyncedAt) FROM departments") + suspend fun maxSyncedAt(): Long? + + @Query("SELECT DISTINCT faculty FROM departments ORDER BY faculty") + suspend fun getFaculties(): List + + @Query("DELETE FROM department_periods") + suspend fun deleteAllPeriods() + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertPeriods(periods: List) + + @Query( + "SELECT * FROM department_periods WHERE weekStart <= :epochDay AND weekEnd >= :epochDay ORDER BY departmentId" + ) + suspend fun getPeriodsForDate(epochDay: Long): List + + @Query("SELECT * FROM department_periods WHERE departmentId = :departmentId ORDER BY weekStart") + suspend fun getPeriodsForDepartment(departmentId: String): List +} diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherDayEntity.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherDayEntity.kt new file mode 100644 index 0000000..c516b8a --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherDayEntity.kt @@ -0,0 +1,27 @@ +package agzam.vyatsu.data.local + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "teacher_days", + foreignKeys = [ + ForeignKey( + entity = TeacherScheduleEntity::class, + parentColumns = ["id"], + childColumns = ["scheduleId"], + onDelete = ForeignKey.CASCADE + ) + ], + indices = [ + Index("scheduleId"), + Index("date") + ] +) +data class TeacherDayEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val scheduleId: Long, + val date: Long +) diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherLessonEntity.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherLessonEntity.kt new file mode 100644 index 0000000..d84c905 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherLessonEntity.kt @@ -0,0 +1,41 @@ +package agzam.vyatsu.data.local + +import agzam.vyatsu.domain.model.LessonType +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "teacher_lessons", + foreignKeys = [ + ForeignKey( + entity = TeacherDayEntity::class, + parentColumns = ["id"], + childColumns = ["dayId"], + onDelete = ForeignKey.CASCADE + ) + ], + indices = [ + Index("dayId"), + Index("teacher"), + Index("room"), + Index("subject") + ] +) +data class TeacherLessonEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val dayId: Long, + val date: Long, + val slotIndex: Int, + val start: Int, + val end: Int, + val lessonIndex: Int, + val raw: String, + val room: String?, + val groupName: String?, + val subgroup: String?, + val subject: String?, + val type: LessonType, + val teacher: String +) diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt new file mode 100644 index 0000000..e995c8d --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleDao.kt @@ -0,0 +1,66 @@ +package agzam.vyatsu.data.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import kotlinx.coroutines.flow.Flow + +@Dao +interface TeacherScheduleDao { + + @Query("SELECT * FROM teacher_schedules ORDER BY weekStart DESC") + fun observeSchedules(): Flow> + + @Query("SELECT * FROM teacher_schedules ORDER BY weekStart DESC") + suspend fun getSchedules(): List + + @Query("DELETE FROM teacher_days WHERE scheduleId = :scheduleId") + suspend fun deleteDaysForSchedule(scheduleId: Long) + + @Insert(onConflict = OnConflictStrategy.ABORT) + suspend fun insertSchedule(schedule: TeacherScheduleEntity): Long + + @Query( + "DELETE FROM teacher_schedules WHERE departmentId = :departmentId AND season = :season AND weekStart = :weekStart" + ) + suspend fun deleteSchedule(departmentId: String, season: String, weekStart: Long) + + @Query("DELETE FROM teacher_schedules WHERE season = :season AND weekStart = :weekStart") + suspend fun deletePeriod(season: String, weekStart: Long) + + @Query("DELETE FROM teacher_schedules") + suspend fun deleteAll() + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertDays(days: List): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertLessons(lessons: List) + + @Query( + "SELECT teacher_days.* FROM teacher_days INNER JOIN teacher_schedules ON teacher_schedules.id = teacher_days.scheduleId " + + "WHERE teacher_schedules.season = :season AND teacher_schedules.weekStart = :weekStart ORDER BY teacher_days.date" + ) + 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 " + + "INNER JOIN teacher_schedules ON teacher_schedules.id = teacher_days.scheduleId " + + "WHERE teacher_schedules.season = :season AND teacher_schedules.weekStart = :weekStart " + + "ORDER BY teacher_lessons.date, teacher_lessons.slotIndex, teacher_lessons.lessonIndex" + ) + 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 " + + "INNER JOIN teacher_schedules ON teacher_schedules.id = teacher_days.scheduleId " + + "WHERE teacher_schedules.season = :season AND teacher_schedules.weekStart = :weekStart ORDER BY teacher" + ) + fun observeDistinctTeachers(season: String, weekStart: Long): Flow> + + @Query( + "SELECT EXISTS(SELECT 1 FROM teacher_schedules WHERE season = :season AND weekStart = :weekStart)" + ) + suspend fun hasSchedulesForPeriod(season: String, weekStart: Long): Boolean +} diff --git a/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleEntity.kt b/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleEntity.kt new file mode 100644 index 0000000..fcb57f2 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/data/local/TeacherScheduleEntity.kt @@ -0,0 +1,31 @@ +package agzam.vyatsu.data.local + +import agzam.vyatsu.domain.model.Season +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "teacher_schedules", + foreignKeys = [ + ForeignKey( + entity = DepartmentEntity::class, + parentColumns = ["departmentId"], + childColumns = ["departmentId"], + onDelete = ForeignKey.CASCADE + ) + ], + indices = [ + Index("departmentId"), + Index(value = ["departmentId", "season", "weekStart"], unique = true) + ] +) +data class TeacherScheduleEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val departmentId: String, + val season: Season, + val weekStart: Long, + val weekEnd: Long, + val lastSyncedAt: Long +) diff --git a/app/src/main/java/agzam/vyatsu/data/repository/PreferencesRepositoryImpl.kt b/app/src/main/java/agzam/vyatsu/data/repository/PreferencesRepositoryImpl.kt index a2cf860..d06b63f 100644 --- a/app/src/main/java/agzam/vyatsu/data/repository/PreferencesRepositoryImpl.kt +++ b/app/src/main/java/agzam/vyatsu/data/repository/PreferencesRepositoryImpl.kt @@ -14,6 +14,16 @@ class PreferencesRepositoryImpl( override suspend fun selectedDayDate(): String? = dao.selectedDayDate() override suspend fun setSelectedDayDate(date: String) = dao.setSelectedDayDate(date) + override suspend fun selectedTeacherId(): String? = dao.selectedTeacherId() + override suspend fun setSelectedTeacherId(id: String) = dao.setSelectedTeacherId(id) + override suspend fun selectedTeacherWeekStart(): String? = dao.selectedTeacherWeekStart() + override suspend fun setSelectedTeacherWeekStart(weekStart: String) = + dao.setSelectedTeacherWeekStart(weekStart) + override suspend fun selectedTeacherDayDate(): String? = dao.selectedTeacherDayDate() + override suspend fun setSelectedTeacherDayDate(date: String) = + dao.setSelectedTeacherDayDate(date) + override suspend fun clearSelectedTeacher() = dao.clearSelectedTeacher() + override suspend fun autoUpdateEnabled(): Boolean = dao.autoUpdateEnabled() == "true" override suspend fun setAutoUpdateEnabled(enabled: Boolean) = dao.setAutoUpdateEnabled(enabled.toString())