diff --git a/.gitignore b/.gitignore
index 921bd0a9a..980085e38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,6 +65,8 @@ captures/
.idea/uiDesigner.xml
.idea/runConfigurations.xml
.idea/discord.xml
+.idea/migrations.xml
+.idea/androidTestResultsUserPreferences.xml
# Keystore files
*.jks
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
deleted file mode 100644
index f8051a6f9..000000000
--- a/.idea/migrations.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 7b290e638..766bee8dd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -27,8 +27,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 34
- versionCode 145
- versionName "2.3.5"
+ versionCode 146
+ versionName "2.4.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -142,7 +142,9 @@ android {
packagingOptions {
resources {
excludes += ['META-INF/library_release.kotlin_module',
- 'META-INF/library-core_release.kotlin_module']
+ 'META-INF/library-core_release.kotlin_module',
+ 'META-INF/LICENSE.md',
+ 'META-INF/LICENSE-notice.md']
}
}
@@ -162,7 +164,7 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = ReleaseStatus.IN_PROGRESS
- userFraction = 0.15d
+ userFraction = 0.50d
updatePriority = 1
enabled.set(false)
}
@@ -193,7 +195,7 @@ ext {
}
dependencies {
- implementation 'io.github.wulkanowy:sdk:2.3.7'
+ implementation 'io.github.wulkanowy:sdk:2.4.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
@@ -250,7 +252,7 @@ dependencies {
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
implementation 'org.apache.commons:commons-text:1.11.0'
- playImplementation platform('com.google.firebase:firebase-bom:32.7.0')
+ playImplementation platform('com.google.firebase:firebase-bom:32.7.1')
playImplementation 'com.google.firebase:firebase-analytics'
playImplementation 'com.google.firebase:firebase-messaging'
playImplementation 'com.google.firebase:firebase-crashlytics:'
@@ -260,9 +262,9 @@ dependencies {
playImplementation "com.google.android.play:integrity:1.3.0"
playImplementation 'com.google.android.play:app-update-ktx:2.1.0'
playImplementation 'com.google.android.play:review-ktx:2.0.1'
- playImplementation "com.google.android.ump:user-messaging-platform:2.1.0"
+ playImplementation "com.google.android.ump:user-messaging-platform:2.2.0"
- hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.301'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.303'
releaseImplementation "com.github.chuckerteam.chucker:library-no-op:$chucker"
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/59.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/59.json
new file mode 100644
index 000000000..a3f2e0dc6
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/59.json
@@ -0,0 +1,2501 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 59,
+ "identityHash": "3bd95e40b587e8131a2a2c23aee538c1",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `scrapper_domain_suffix` TEXT NOT NULL DEFAULT '', `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "scrapperDomainSuffix",
+ "columnName": "scrapper_domain_suffix",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Students_email_symbol_student_id_school_id_class_id",
+ "unique": true,
+ "columnNames": [
+ "email",
+ "symbol",
+ "student_id",
+ "school_id",
+ "class_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Semesters",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timeId",
+ "columnName": "time_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excused",
+ "columnName": "excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excusable",
+ "columnName": "excusable",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excuseStatus",
+ "columnName": "excuse_status",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subject_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "month",
+ "columnName": "month",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceExcused",
+ "columnName": "absence_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceForSchoolReasons",
+ "columnName": "absence_for_school_reasons",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "latenessExcused",
+ "columnName": "lateness_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entry",
+ "columnName": "entry",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "modifier",
+ "columnName": "modifier",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gradeSymbol",
+ "columnName": "grade_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weight",
+ "columnName": "weight",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weightValue",
+ "columnName": "weightValue",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGrade",
+ "columnName": "predicted_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "final_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "proposedPoints",
+ "columnName": "proposed_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalPoints",
+ "columnName": "final_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pointsSum",
+ "columnName": "points_sum",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "average",
+ "columnName": "average",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "others",
+ "columnName": "others",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "student",
+ "columnName": "student",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mailboxKey",
+ "columnName": "mailbox_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondents",
+ "columnName": "correspondents",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recipients",
+ "columnName": "recipients",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))",
+ "fields": [
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "message_global_key",
+ "url",
+ "filename"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "luckyNumber",
+ "columnName": "lucky_number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "topic",
+ "columnName": "topic",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "substitution",
+ "columnName": "substitution",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "resources",
+ "columnName": "resources",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Mailboxes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))",
+ "fields": [
+ {
+ "fieldPath": "globalKey",
+ "columnName": "globalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolId",
+ "columnName": "schoolId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "studentName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolNameShort",
+ "columnName": "schoolNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "globalKey"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "mailboxGlobalKey",
+ "columnName": "mailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentMailboxGlobalKey",
+ "columnName": "studentMailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "schoolShortName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "contact",
+ "columnName": "contact",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "headmaster",
+ "columnName": "headmaster",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pedagogue",
+ "columnName": "pedagogue",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `types` TEXT NOT NULL DEFAULT '[]', `is_ok_visible` INTEGER NOT NULL DEFAULT 0, `is_x_visible` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "types",
+ "columnName": "types",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'[]'"
+ },
+ {
+ "fieldPath": "isOkVisible",
+ "columnName": "is_ok_visible",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "isXVisible",
+ "columnName": "is_x_visible",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesDescriptive",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `description` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "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, '3bd95e40b587e8131a2a2c23aee538c1')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt
index 0c47e6bb6..1b0319f69 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt
+++ b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt
@@ -14,34 +14,37 @@ import kotlin.test.assertFailsWith
@RunWith(AndroidJUnit4::class)
class ScramblerTest {
+ private val scrambler = Scrambler(ApplicationProvider.getApplicationContext())
+
@Test
fun encryptDecryptTest() {
- assertEquals("TEST", decrypt(encrypt("TEST",
- ApplicationProvider.getApplicationContext())))
+ assertEquals(
+ "TEST", scrambler.decrypt(scrambler.encrypt("TEST"))
+ )
}
@Test
fun emptyTextEncryptTest() {
assertFailsWith {
- decrypt("")
+ scrambler.decrypt("")
}
assertFailsWith {
- encrypt("", ApplicationProvider.getApplicationContext())
+ scrambler.encrypt("")
}
}
@Test
@SdkSuppress(minSdkVersion = 18)
fun emptyKeyStoreTest() {
- val text = encrypt("test", ApplicationProvider.getApplicationContext())
+ val text = scrambler.encrypt("test")
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
keyStore.deleteEntry("wulkanowy_password")
assertFailsWith {
- decrypt(text)
+ scrambler.decrypt(text)
}
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 174c9a1fc..f43dfdd2c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -44,6 +44,7 @@
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="false"
android:theme="@style/WulkanowyTheme"
+ android:resizeableActivity="true"
tools:ignore="DataExtractionRules,UnusedAttribute">
{
+
+ @Query("SELECT * FROM GradesDescriptive WHERE semester_id = :semesterId AND student_id = :studentId")
+ fun loadAll(semesterId: Int, studentId: Int): Flow>
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeDescriptive.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeDescriptive.kt
new file mode 100644
index 000000000..9aec9599a
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeDescriptive.kt
@@ -0,0 +1,27 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.io.Serializable
+
+@Entity(tableName = "GradesDescriptive")
+data class GradeDescriptive(
+
+ @ColumnInfo(name = "semester_id")
+ val semesterId: Int,
+
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
+
+ val subject: String,
+
+ val description: String,
+) : Serializable {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+
+ @ColumnInfo(name = "is_notified")
+ var isNotified: Boolean = true
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt
index 178de682a..66e922171 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt
@@ -1,10 +1,12 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.sdk.pojo.GradeSummary as SdkGradeSummary
import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade
+import io.github.wulkanowy.sdk.pojo.GradeDescriptive as SdkGradeDescriptive
+import io.github.wulkanowy.sdk.pojo.GradeSummary as SdkGradeSummary
fun List.mapToEntities(semester: Semester) = map {
Grade(
@@ -40,3 +42,15 @@ fun List.mapToEntities(semester: Semester) = map {
average = it.average
)
}
+
+@JvmName("mapGradeDescriptiveToEntities")
+fun List.mapToEntities(semester: Semester) = map {
+ GradeDescriptive(
+ semesterId = semester.semesterId,
+ studentId = semester.studentId,
+ subject = it.subject,
+ description = it.description
+ )
+}
+
+
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
index b2bb7b906..1e2ea9354 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
@@ -1,15 +1,22 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.GradeDao
+import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.switchSemester
+import io.github.wulkanowy.utils.toLocalDate
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
@@ -22,14 +29,13 @@ import javax.inject.Singleton
class GradeRepository @Inject constructor(
private val gradeDb: GradeDao,
private val gradeSummaryDb: GradeSummaryDao,
+ private val gradeDescriptiveDb: GradeDescriptiveDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val saveFetchResultMutex = Mutex()
- private val cacheKey = "grade"
-
fun getGrades(
student: Student,
semester: Semester,
@@ -41,30 +47,52 @@ class GradeRepository @Inject constructor(
//When details is empty and summary is not, app will not use summary cache - edge case
it.first.isEmpty()
},
- shouldFetch = { (details, summaries) ->
- val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
- details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired
+ shouldFetch = { (details, summaries, descriptive) ->
+ val isExpired =
+ refreshHelper.shouldBeRefreshed(getRefreshKey(GRADE_CACHE_KEY, semester))
+ details.isEmpty() || (summaries.isEmpty() && descriptive.isEmpty()) || forceRefresh || isExpired
},
query = {
val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId)
val summaryFlow = gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
- detailsFlow.combine(summaryFlow) { details, summaries -> details to summaries }
+ val descriptiveFlow =
+ gradeDescriptiveDb.loadAll(semester.semesterId, semester.studentId)
+
+ combine(detailsFlow, summaryFlow, descriptiveFlow) { details, summaries, descriptive ->
+ Triple(details, summaries, descriptive)
+ }
},
fetch = {
- val (details, summary) = sdk.init(student)
+ val (details, summary, descriptive) = sdk.init(student)
.switchSemester(semester)
.getGrades(semester.semesterId)
- details.mapToEntities(semester) to summary.mapToEntities(semester)
+ Triple(
+ details.mapToEntities(semester),
+ summary.mapToEntities(semester),
+ descriptive.mapToEntities(semester)
+ )
},
- saveFetchResult = { (oldDetails, oldSummary), (newDetails, newSummary) ->
+ saveFetchResult = { (oldDetails, oldSummary, oldDescriptive), (newDetails, newSummary, newDescriptive) ->
refreshGradeDetails(student, oldDetails, newDetails, notify)
refreshGradeSummaries(oldSummary, newSummary, notify)
+ refreshGradeDescriptions(oldDescriptive, newDescriptive, notify)
- refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
+ refreshHelper.updateLastRefreshTimestamp(getRefreshKey(GRADE_CACHE_KEY, semester))
}
)
+ private suspend fun refreshGradeDescriptions(
+ old: List,
+ new: List,
+ notify: Boolean
+ ) {
+ gradeDescriptiveDb.deleteAll(old uniqueSubtract new)
+ gradeDescriptiveDb.insertAll((new uniqueSubtract old).onEach {
+ if (notify) it.isNotified = false
+ })
+ }
+
private suspend fun refreshGradeDetails(
student: Student,
oldGrades: List,
@@ -132,6 +160,10 @@ class GradeRepository @Inject constructor(
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
}
+ fun getGradesDescriptiveFromDatabase(semester: Semester): Flow> {
+ return gradeDescriptiveDb.loadAll(semester.semesterId, semester.studentId)
+ }
+
suspend fun updateGrade(grade: Grade) {
return gradeDb.updateAll(listOf(grade))
}
@@ -143,4 +175,13 @@ class GradeRepository @Inject constructor(
suspend fun updateGradesSummary(gradesSummary: List) {
return gradeSummaryDb.updateAll(gradesSummary)
}
+
+ suspend fun updateGradesDescriptive(gradesDescriptive: List) {
+ return gradeDescriptiveDb.updateAll(gradesDescriptive)
+ }
+
+ private companion object {
+
+ private const val GRADE_CACHE_KEY = "grade"
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt
new file mode 100644
index 000000000..efe928e2b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt
@@ -0,0 +1,33 @@
+package io.github.wulkanowy.domain.timetable
+
+import io.github.wulkanowy.data.dataOrNull
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.TimetableRepository
+import io.github.wulkanowy.data.toFirstResult
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.sunday
+import java.time.LocalDate
+import javax.inject.Inject
+
+class IsStudentHasLessonsOnWeekendUseCase @Inject constructor(
+ private val timetableRepository: TimetableRepository,
+ private val isWeekendHasLessonsUseCase: IsWeekendHasLessonsUseCase,
+) {
+
+ suspend operator fun invoke(
+ student: Student,
+ semester: Semester,
+ currentDate: LocalDate = LocalDate.now(),
+ ): Boolean {
+ val lessons = timetableRepository.getTimetable(
+ student = student,
+ semester = semester,
+ start = currentDate.monday,
+ end = currentDate.sunday,
+ forceRefresh = false,
+ timetableType = TimetableRepository.TimetableType.NORMAL
+ ).toFirstResult().dataOrNull?.lessons.orEmpty()
+ return isWeekendHasLessonsUseCase(lessons)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/domain/timetable/IsWeekendHasLessonsUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/timetable/IsWeekendHasLessonsUseCase.kt
new file mode 100644
index 000000000..908c9df78
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/domain/timetable/IsWeekendHasLessonsUseCase.kt
@@ -0,0 +1,17 @@
+package io.github.wulkanowy.domain.timetable
+
+import io.github.wulkanowy.data.db.entities.Timetable
+import java.time.DayOfWeek
+import javax.inject.Inject
+
+class IsWeekendHasLessonsUseCase @Inject constructor() {
+
+ operator fun invoke(
+ lessons: List,
+ ): Boolean = lessons.any {
+ it.date.dayOfWeek in listOf(
+ DayOfWeek.SATURDAY,
+ DayOfWeek.SUNDAY,
+ )
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
index 9b49ed178..7f90bbddc 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
@@ -4,12 +4,12 @@ import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GroupNotificationData
import io.github.wulkanowy.data.pojos.NotificationData
import io.github.wulkanowy.ui.modules.Destination
-import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import javax.inject.Inject
@@ -88,4 +88,28 @@ class NewGradeNotification @Inject constructor(
appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
}
+
+ suspend fun notifyDescriptive(items: List, student: Student) {
+ val notificationDataList = items.map {
+ NotificationData(
+ title = context.getPlural(R.plurals.grade_new_items_descriptive, 1),
+ content = "${it.subject}: ${it.description}",
+ destination = Destination.Grade,
+ )
+ }
+
+ val groupNotificationData = GroupNotificationData(
+ notificationDataList = notificationDataList,
+ title = context.getPlural(R.plurals.grade_new_items_descriptive, items.size),
+ content = context.getPlural(
+ R.plurals.grade_notify_new_items_descriptive,
+ items.size,
+ items.size
+ ),
+ destination = Destination.Grade,
+ type = NotificationType.NEW_GRADE_DESCRIPTIVE
+ )
+
+ appNotificationManager.sendMultipleNotifications(groupNotificationData, student)
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt
index 023ae2e4c..4e7f27351 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt
@@ -37,6 +37,10 @@ enum class NotificationType(
channel = NewGradesChannel.CHANNEL_ID,
icon = R.drawable.ic_stat_grade,
),
+ NEW_GRADE_DESCRIPTIVE(
+ channel = NewGradesChannel.CHANNEL_ID,
+ icon = R.drawable.ic_stat_grade,
+ ),
NEW_HOMEWORK(
channel = NewHomeworkChannel.CHANNEL_ID,
icon = R.drawable.ic_more_homework,
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
index ba21b8600..b62ad94b9 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
@@ -45,5 +45,15 @@ class GradeWork @Inject constructor(
grade.isFinalGradeNotified = true
})
}
+
+ gradeRepository.getGradesDescriptiveFromDatabase(semester).first()
+ .filter { !it.isNotified }
+ .let {
+ if (it.isNotEmpty()) newGradeNotification.notifyDescriptive(it, student)
+
+ gradeRepository.updateGradesDescriptive(it.onEach { grade ->
+ grade.isNotified = true
+ })
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt
index 39f376f65..4e9baac3a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt
@@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.attendance
+import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -10,6 +11,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.enums.SentExcuseStatus
import io.github.wulkanowy.databinding.ItemAttendanceBinding
import io.github.wulkanowy.utils.descriptionRes
+import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.isExcusableOrNotExcused
import javax.inject.Inject
@@ -39,7 +41,33 @@ class AttendanceAdapter @Inject constructor() :
root.context.getString(R.string.all_no_data)
}
attendanceItemDescription.setText(item.descriptionRes)
- attendanceItemAlert.isVisible = item.let { it.absence && !it.excused }
+
+ attendanceItemDescription.setTextColor(
+ root.context.getThemeAttrColor(
+ when {
+ item.absence && !item.excused -> R.attr.colorAttendanceAbsence
+ item.lateness && !item.excused -> R.attr.colorAttendanceLateness
+ else -> android.R.attr.textColorSecondary
+ }
+ )
+ )
+
+ if (item.exemption || item.excused) {
+ attendanceItemDescription.setTypeface(null, Typeface.BOLD)
+ } else {
+ attendanceItemDescription.setTypeface(null, Typeface.NORMAL)
+ }
+
+ attendanceItemAlert.isVisible =
+ item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) }
+
+ attendanceItemAlert.setColorFilter(root.context.getThemeAttrColor(
+ when{
+ item.absence && !item.excused -> R.attr.colorAttendanceAbsence
+ item.lateness && !item.excused -> R.attr.colorAttendanceLateness
+ else -> android.R.attr.colorPrimary
+ }
+ ))
attendanceItemNumber.visibility = View.GONE
attendanceItemExcuseInfo.visibility = View.GONE
attendanceItemExcuseCheckbox.visibility = View.GONE
@@ -54,10 +82,12 @@ class AttendanceAdapter @Inject constructor() :
attendanceItemExcuseInfo.visibility = View.VISIBLE
attendanceItemAlert.visibility = View.INVISIBLE
}
+
SentExcuseStatus.DENIED -> {
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied)
attendanceItemExcuseInfo.visibility = View.VISIBLE
}
+
else -> {
if (item.isExcusableOrNotExcused && excuseActionMode) {
attendanceItemNumber.visibility = View.GONE
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt
index c0026bee5..635033132 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt
@@ -6,10 +6,12 @@ import android.view.View
import androidx.core.os.bundleOf
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.databinding.DialogAttendanceBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.utils.descriptionRes
+import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.serializable
import io.github.wulkanowy.utils.toFormattedString
@@ -44,6 +46,16 @@ class AttendanceDialog : BaseDialogFragment() {
with(binding) {
attendanceDialogSubjectValue.text = attendance.subject
attendanceDialogDescriptionValue.setText(attendance.descriptionRes)
+ attendanceDialogDescriptionValue.setTextColor(
+ root.context.getThemeAttrColor(
+ when {
+ attendance.absence && !attendance.excused -> R.attr.colorAttendanceAbsence
+ attendance.lateness && !attendance.excused -> R.attr.colorAttendanceLateness
+ else -> android.R.attr.textColorSecondary
+ }
+ )
+ )
+
attendanceDialogDateValue.text = attendance.date.toFormattedString()
attendanceDialogNumberValue.text = attendance.number.toString()
attendanceDialogClose.setOnClickListener { dismiss() }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index ad604499b..1e6f1c198 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -24,11 +24,13 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
+import io.github.wulkanowy.domain.timetable.IsStudentHasLessonsOnWeekendUseCase
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AdsHelper
import io.github.wulkanowy.utils.calculatePercentage
import io.github.wulkanowy.utils.nextOrSameSchoolDay
+import io.github.wulkanowy.utils.sunday
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
@@ -56,6 +58,7 @@ class DashboardPresenter @Inject constructor(
private val messageRepository: MessageRepository,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val timetableRepository: TimetableRepository,
+ private val isStudentHasLessonsOnWeekendUseCase: IsStudentHasLessonsOnWeekendUseCase,
private val homeworkRepository: HomeworkRepository,
private val examRepository: ExamRepository,
private val conferenceRepository: ConferenceRepository,
@@ -435,14 +438,17 @@ class DashboardPresenter @Inject constructor(
private fun loadLessons(student: Student, forceRefresh: Boolean) {
flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
- val date = LocalDate.now().nextOrSameSchoolDay
+ val date = when (isStudentHasLessonsOnWeekendUseCase(student, semester)) {
+ true -> LocalDate.now()
+ else -> LocalDate.now().nextOrSameSchoolDay
+ }
timetableRepository.getTimetable(
student = student,
semester = semester,
start = date,
- end = date,
- forceRefresh = forceRefresh
+ end = date.sunday,
+ forceRefresh = forceRefresh,
)
}
.onEach {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt
index d0dfcd696..cdd186b94 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt
@@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugAttendanceItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugConferenceItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugExamItems
+import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDescriptiveItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDetailsItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeSummaryItems
import io.github.wulkanowy.ui.modules.debug.notification.mock.debugHomeworkItems
@@ -55,6 +56,14 @@ class NotificationDebugPresenter @Inject constructor(
NotificationDebugItem(R.string.grade_summary_final_grade) { n ->
withStudent { newGradeNotification.notifyFinal(debugGradeSummaryItems.take(n), it) }
},
+ NotificationDebugItem(R.string.grade_summary_descriptive) { n ->
+ withStudent {
+ newGradeNotification.notifyDescriptive(
+ debugGradeDescriptiveItems.take(n),
+ it
+ )
+ }
+ },
NotificationDebugItem(R.string.homework_title) { n ->
withStudent { newHomeworkNotification.notify(debugHomeworkItems.take(n), it) }
},
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDescriptive.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDescriptive.kt
new file mode 100644
index 000000000..d5a0c089b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDescriptive.kt
@@ -0,0 +1,48 @@
+package io.github.wulkanowy.ui.modules.debug.notification.mock
+
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
+
+val debugGradeDescriptiveItems = listOf(
+ generateGradeDescriptive(
+ "Matematyka",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive("Fizyka", "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
+ generateGradeDescriptive(
+ "Geografia",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive(
+ "Sieci komputerowe",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive(
+ "Systemy operacyjne",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive(
+ "Język polski",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive(
+ "Język angielski",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive("Religia", "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
+ generateGradeDescriptive(
+ "Język niemiecki",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+ generateGradeDescriptive(
+ "Wychowanie fizyczne",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ ),
+)
+
+private fun generateGradeDescriptive(subject: String, description: String) =
+ GradeDescriptive(
+ semesterId = 0,
+ studentId = 0,
+ subject = subject,
+ description = description
+ )
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
index ec4bd8e8c..e8a5fa254 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
@@ -1,15 +1,23 @@
package io.github.wulkanowy.ui.modules.grade
-import io.github.wulkanowy.data.*
+import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.errorOrNull
+import io.github.wulkanowy.data.flatResourceFlow
+import io.github.wulkanowy.data.mapData
+import io.github.wulkanowy.data.mapResourceData
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
+import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ALL_YEAR
+import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.BOTH_SEMESTERS
+import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ONE_SEMESTER
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -62,6 +70,7 @@ class GradeAverageProvider @Inject constructor(
forceRefresh = forceRefresh,
params = params,
)
+
BOTH_SEMESTERS -> calculateCombinedAverage(
student = student,
semesters = semesters,
@@ -69,6 +78,7 @@ class GradeAverageProvider @Inject constructor(
forceRefresh = forceRefresh,
config = params,
)
+
ALL_YEAR -> calculateCombinedAverage(
student = student,
semesters = semesters,
@@ -189,36 +199,73 @@ class GradeAverageProvider @Inject constructor(
): Flow>> {
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
.mapResourceData { res ->
- val (details, summaries) = res
+ val (details, summaries, descriptives) = res
val isAnyAverage = summaries.any { it.average != .0 }
val allGrades = details.groupBy { it.subject }
+ val descriptiveGradesBySubject = descriptives.associateBy { it.subject }
- val items = summaries.emulateEmptySummaries(
- student = student,
- semester = semester,
- grades = allGrades.toList(),
- calcAverage = isAnyAverage,
- params = params,
- ).map { summary ->
- val grades = allGrades[summary.subject].orEmpty()
- GradeSubject(
- subject = summary.subject,
- average = if (!isAnyAverage || params.forceAverageCalc) {
- grades.updateModifiers(student, params)
- .calcAverage(params.isOptionalArithmeticAverage)
- } else summary.average,
- points = summary.pointsSum,
- summary = summary,
- grades = grades,
- isVulcanAverage = isAnyAverage
+ val items = summaries
+ .createEmptySummariesByGradesIfNeeded(
+ student = student,
+ semester = semester,
+ grades = allGrades.toList(),
+ calcAverage = isAnyAverage,
+ params = params,
)
- }
+ .createEmptySummariesByDescriptiveGradesIfNeeded(
+ student = student,
+ semester = semester,
+ descriptives = descriptives,
+ )
+ .map { summary ->
+ val grades = allGrades[summary.subject].orEmpty()
+ val descriptiveGrade = descriptiveGradesBySubject[summary.subject]
+
+ GradeSubject(
+ subject = summary.subject,
+ average = if (!isAnyAverage || params.forceAverageCalc) {
+ grades.updateModifiers(student, params)
+ .calcAverage(params.isOptionalArithmeticAverage)
+ } else summary.average,
+ points = summary.pointsSum,
+ summary = summary,
+ grades = grades,
+ descriptive = descriptiveGrade,
+ isVulcanAverage = isAnyAverage
+ )
+ }
items
}
}
- private fun List.emulateEmptySummaries(
+ private fun List.createEmptySummariesByDescriptiveGradesIfNeeded(
+ student: Student,
+ semester: Semester,
+ descriptives: List
+ ): List {
+ val summarySubjects = this.map { it.subject }
+ val gradeSummaryToAdd = descriptives.mapNotNull { gradeDescriptive ->
+ if (gradeDescriptive.subject in summarySubjects) return@mapNotNull null
+
+ GradeSummary(
+ studentId = student.studentId,
+ semesterId = semester.semesterId,
+ position = 0,
+ subject = gradeDescriptive.subject,
+ predictedGrade = "",
+ finalGrade = "",
+ proposedPoints = "",
+ finalPoints = "",
+ pointsSum = "",
+ average = .0
+ )
+ }
+
+ return this + gradeSummaryToAdd
+ }
+
+ private fun List.createEmptySummariesByGradesIfNeeded(
student: Student,
semester: Semester,
grades: List>>,
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt
index 57be55ee3..a465551f9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSubject.kt
@@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Grade
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
import io.github.wulkanowy.data.db.entities.GradeSummary
data class GradeSubject(
@@ -8,6 +9,7 @@ data class GradeSubject(
val average: Double,
val points: String,
val summary: GradeSummary,
+ val descriptive: GradeDescriptive?,
val grades: List,
val isVulcanAverage: Boolean
)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
index 4261c507d..d9621f51e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
@@ -1,13 +1,22 @@
package io.github.wulkanowy.ui.modules.grade.details
-import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeExpandMode
-import io.github.wulkanowy.data.enums.GradeSortingMode.*
+import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
+import io.github.wulkanowy.data.enums.GradeSortingMode.AVERAGE
+import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
+import io.github.wulkanowy.data.flatResourceFlow
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceData
+import io.github.wulkanowy.data.onResourceError
+import io.github.wulkanowy.data.onResourceIntermediate
+import io.github.wulkanowy.data.onResourceNotLoading
+import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
@@ -207,20 +216,20 @@ class GradeDetailsPresenter @Inject constructor(
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
}
}
- .map { (subject, average, points, _, grades) ->
- val subItems = grades
+ .map { gradeSubject ->
+ val subItems = gradeSubject.grades
.sortedByDescending { it.date }
.map { GradeDetailsItem(it, ViewType.ITEM) }
val gradeDetailsItems = listOf(
GradeDetailsItem(
GradeDetailsHeader(
- subject = subject,
- average = average,
- pointsSum = points,
+ subject = gradeSubject.subject,
+ average = gradeSubject.average,
+ pointsSum = gradeSubject.points,
grades = subItems
).apply {
- newGrades = grades.filter { grade -> !grade.isRead }.size
+ newGrades = gradeSubject.grades.filter { grade -> !grade.isRead }.size
}, ViewType.HEADER
)
)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt
index 8dcade56e..95cf97bed 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt
@@ -2,16 +2,16 @@ package io.github.wulkanowy.ui.modules.grade.summary
import android.annotation.SuppressLint
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.ItemGradeSummaryBinding
import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding
import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid
import io.github.wulkanowy.utils.calcFinalAverage
+import io.github.wulkanowy.utils.ifNullOrBlank
import java.util.Locale
import javax.inject.Inject
@@ -24,7 +24,7 @@ class GradeSummaryAdapter @Inject constructor(
ITEM(2)
}
- var items = emptyList()
+ var items = emptyList()
var onCalculatedHelpClickListener: () -> Unit = {}
@@ -44,9 +44,11 @@ class GradeSummaryAdapter @Inject constructor(
ViewType.HEADER.id -> HeaderViewHolder(
ScrollableHeaderGradeSummaryBinding.inflate(inflater, parent, false)
)
+
ViewType.ITEM.id -> ItemViewHolder(
ItemGradeSummaryBinding.inflate(inflater, parent, false)
)
+
else -> throw IllegalStateException()
}
}
@@ -60,19 +62,23 @@ class GradeSummaryAdapter @Inject constructor(
private fun bindHeaderViewHolder(binding: ScrollableHeaderGradeSummaryBinding) {
if (items.isEmpty()) return
+ val gradeSummaries = items
+ .filter { it.gradeDescriptive == null }
+ .map { it.gradeSummary }
val context = binding.root.context
- val finalItemsCount = items.count { isGradeValid(it.finalGrade) }
- val calculatedItemsCount = items.count { value -> value.average != 0.0 }
- val allItemsCount = items.count { !it.subject.equals("zachowanie", true) }
- val finalAverage = items.calcFinalAverage(
+ val finalItemsCount = gradeSummaries.count { isGradeValid(it.finalGrade) }
+ val calculatedItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
+ val allItemsCount = gradeSummaries.count { !it.subject.equals("zachowanie", true) }
+ val finalAverage = gradeSummaries.calcFinalAverage(
preferencesRepository.gradePlusModifier,
preferencesRepository.gradeMinusModifier
)
- val calculatedAverage = items.filter { value -> value.average != 0.0 }
+ val calculatedAverage = gradeSummaries.filter { value -> value.average != 0.0 }
.map { values -> values.average }
.reversed() // fix average precision
.average()
+ .let { if (it.isNaN()) 0.0 else it }
with(binding) {
gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage)
@@ -95,16 +101,28 @@ class GradeSummaryAdapter @Inject constructor(
}
@SuppressLint("SetTextI18n")
- private fun bindItemViewHolder(binding: ItemGradeSummaryBinding, item: GradeSummary) {
- with(binding) {
- gradeSummaryItemTitle.text = item.subject
- gradeSummaryItemPoints.text = item.pointsSum
- gradeSummaryItemAverage.text = formatAverage(item.average, "")
- gradeSummaryItemPredicted.text = "${item.predictedGrade} ${item.proposedPoints}".trim()
- gradeSummaryItemFinal.text = "${item.finalGrade} ${item.finalPoints}".trim()
+ private fun bindItemViewHolder(binding: ItemGradeSummaryBinding, item: GradeSummaryItem) {
+ val (gradeSummary, gradeDescriptive) = item
- gradeSummaryItemPointsContainer.visibility =
- if (item.pointsSum.isBlank()) View.GONE else View.VISIBLE
+ with(binding) {
+ gradeSummaryItemTitle.text = gradeSummary.subject
+ gradeSummaryItemPoints.text = gradeSummary.pointsSum
+ gradeSummaryItemAverage.text = formatAverage(gradeSummary.average, "")
+ gradeSummaryItemPredicted.text =
+ "${gradeSummary.predictedGrade} ${gradeSummary.proposedPoints}".trim()
+ gradeSummaryItemFinal.text =
+ "${gradeSummary.finalGrade} ${gradeSummary.finalPoints}".trim()
+ gradeSummaryItemDescriptive.text = gradeDescriptive?.description.ifNullOrBlank {
+ root.context.getString(R.string.all_no_data)
+ }
+
+ gradeSummaryItemFinalDivider.isVisible = gradeDescriptive == null
+ gradeSummaryItemPredictedDivider.isVisible = gradeDescriptive == null
+ gradeSummaryItemPointsDivider.isVisible = gradeDescriptive == null
+ gradeSummaryItemPredictedContainer.isVisible = gradeDescriptive == null
+ gradeSummaryItemFinalContainer.isVisible = gradeDescriptive == null
+ gradeSummaryItemDescriptiveContainer.isVisible = gradeDescriptive != null
+ gradeSummaryItemPointsContainer.isVisible = gradeSummary.pointsSum.isNotBlank()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt
index abd0b13c4..35b2edd58 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt
@@ -5,12 +5,10 @@ import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
-import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.databinding.FragmentGradeSummaryBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
@@ -72,7 +70,7 @@ class GradeSummaryFragment :
}
}
- override fun updateData(data: List) {
+ override fun updateData(data: List) {
with(gradeSummaryAdapter) {
items = data
notifyDataSetChanged()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt
new file mode 100644
index 000000000..cf0f1d92e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt
@@ -0,0 +1,9 @@
+package io.github.wulkanowy.ui.modules.grade.summary
+
+import io.github.wulkanowy.data.db.entities.GradeDescriptive
+import io.github.wulkanowy.data.db.entities.GradeSummary
+
+data class GradeSummaryItem(
+ val gradeSummary: GradeSummary,
+ val gradeDescriptive: GradeDescriptive?
+)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
index 32508ff6f..d762df02b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
@@ -1,9 +1,16 @@
package io.github.wulkanowy.ui.modules.grade.summary
-import io.github.wulkanowy.data.*
-import io.github.wulkanowy.data.db.entities.GradeSummary
-import io.github.wulkanowy.data.enums.GradeSortingMode
-import io.github.wulkanowy.data.enums.GradeSortingMode.*
+import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
+import io.github.wulkanowy.data.enums.GradeSortingMode.AVERAGE
+import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
+import io.github.wulkanowy.data.flatResourceFlow
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.mapResourceData
+import io.github.wulkanowy.data.onResourceData
+import io.github.wulkanowy.data.onResourceError
+import io.github.wulkanowy.data.onResourceIntermediate
+import io.github.wulkanowy.data.onResourceNotLoading
+import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@@ -128,7 +135,7 @@ class GradeSummaryPresenter @Inject constructor(
view?.showFinalAverageHelpDialog()
}
- private fun createGradeSummaryItems(items: List): List {
+ private fun createGradeSummaryItems(items: List): List {
return items
.filter { !checkEmpty(it) }
.let { gradeSubjects ->
@@ -136,21 +143,32 @@ class GradeSummaryPresenter @Inject constructor(
DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage ->
gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date
}
+
ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
gradeDetailsWithAverage.subject.lowercase()
}
+
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
}
}
- .map { it.summary.copy(average = it.average) }
+ .map {
+ val gradeSummary = it.summary.copy(average = it.average)
+ val descriptive = it.descriptive
+ GradeSummaryItem(
+ gradeSummary = gradeSummary,
+ gradeDescriptive = descriptive,
+ )
+ }
+
}
private fun checkEmpty(gradeSummary: GradeSubject): Boolean {
return gradeSummary.run {
summary.finalGrade.isBlank()
- && summary.predictedGrade.isBlank()
- && average == .0
- && points.isBlank()
+ && summary.predictedGrade.isBlank()
+ && average == .0
+ && points.isBlank()
+ && descriptive == null
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt
index 156731c31..36bd61421 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.grade.summary
-import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.ui.base.BaseView
interface GradeSummaryView : BaseView {
@@ -13,7 +12,7 @@ interface GradeSummaryView : BaseView {
fun initView()
- fun updateData(data: List)
+ fun updateData(data: List)
fun resetView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt
index 2c11bb6d5..b066cceb9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt
@@ -7,5 +7,6 @@ data class LoginData(
val password: String,
val baseUrl: String,
val domainSuffix: String,
- val symbol: String?,
+ val defaultSymbol: String,
+ val userEnteredSymbol: String? = null,
) : Serializable
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt
index a17ad0035..009f26e17 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt
@@ -71,7 +71,7 @@ class LoginAdvancedPresenter @Inject constructor(
fun updateUsernameLabel() {
view?.apply {
- setUsernameLabel(if ("vulcan" in formHostValue || "fakelog" in formHostValue) emailLabel else nicknameLabel)
+ setUsernameLabel(if ("vulcan" in formHostValue || "wulkanowy" in formHostValue) emailLabel else nicknameLabel)
}
}
@@ -79,7 +79,7 @@ class LoginAdvancedPresenter @Inject constructor(
view?.apply {
clearPassError()
clearUsernameError()
- if (formHostValue.contains("fakelog")) {
+ if (formHostValue.contains("wulkanowy")) {
setDefaultCredentials(
"jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999"
)
@@ -155,7 +155,7 @@ class LoginAdvancedPresenter @Inject constructor(
password = view?.formPassValue.orEmpty().trim(),
baseUrl = view?.formHostValue.orEmpty().trim(),
domainSuffix = view?.formDomainSuffix.orEmpty().trim(),
- symbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(),
+ defaultSymbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(),
)
when (it.data.symbols.size) {
0 -> view?.navigateToSymbol(loginData)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt
index c9ae4f27f..af89f147c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt
@@ -90,7 +90,7 @@ class LoginFormPresenter @Inject constructor(
clearPassError()
clearUsernameError()
clearHostError()
- if (formHostValue.contains("fakelog")) {
+ if (formHostValue.contains("wulkanowy")) {
setCredentials("jan@fakelog.cf", "jan123")
} else if (formUsernameValue == "jan@fakelog.cf" && formPassValue == "jan123") {
setCredentials("", "")
@@ -148,7 +148,7 @@ class LoginFormPresenter @Inject constructor(
password = password,
baseUrl = host,
domainSuffix = domainSuffix,
- symbol = symbol
+ defaultSymbol = symbol
)
}
@@ -167,7 +167,7 @@ class LoginFormPresenter @Inject constructor(
password = loginData.password,
scrapperBaseUrl = loginData.baseUrl,
domainSuffix = loginData.domainSuffix,
- symbol = loginData.symbol.orEmpty(),
+ symbol = loginData.defaultSymbol,
)
}
.logResourceStatus("login")
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt
index a424df40d..18902e014 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt
@@ -38,7 +38,7 @@ class LoginRecoverPresenter @Inject constructor(
fun onHostSelected() {
view?.run {
- if ("fakelog" in recoverHostValue) setDefaultCredentials("jan@fakelog.cf")
+ if ("wulkanowy" in recoverHostValue) setDefaultCredentials("jan@fakelog.cf")
clearUsernameError()
updateFields()
}
@@ -60,7 +60,7 @@ class LoginRecoverPresenter @Inject constructor(
resourceFlow {
recoverRepository.getReCaptchaSiteKey(
host,
- symbol.ifBlank { "Default" })
+ symbol.ifBlank { "default" })
}.onEach {
when (it) {
is Resource.Loading -> view?.run {
@@ -103,7 +103,7 @@ class LoginRecoverPresenter @Inject constructor(
fun onReCaptchaVerified(reCaptchaResponse: String) {
val username = view?.recoverNameValue.orEmpty()
val host = view?.recoverHostValue.orEmpty()
- val symbol = view?.formHostSymbol.ifNullOrBlank { "Default" }
+ val symbol = view?.formHostSymbol.ifNullOrBlank { "default" }
resourceFlow {
recoverRepository.sendRecoverRequest(
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
index 06efd8d98..0fe36aa99 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
@@ -10,13 +10,11 @@ import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
import io.github.wulkanowy.ui.base.BaseFragment
-import io.github.wulkanowy.ui.modules.auth.AuthDialog
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog
import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo
import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.serializable
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt
index 6cbdfbb85..344414180 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt
@@ -111,8 +111,8 @@ class LoginStudentSelectPresenter @Inject constructor(
val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() }
val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() }
- if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.symbol }) {
- add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.symbol }))
+ if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.userEnteredSymbol }) {
+ add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.userEnteredSymbol }))
}
addAll(createNotEmptySymbolItems(notEmptySymbols, students))
@@ -317,7 +317,7 @@ class LoginStudentSelectPresenter @Inject constructor(
loginData = loginData,
registerUser = registerUser,
lastErrorMessage = lastError?.message,
- enteredSymbol = loginData.symbol,
+ enteredSymbol = loginData.userEnteredSymbol,
)
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt
index fcf7f51c9..4be2dbaad 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt
@@ -105,7 +105,7 @@ class LoginSupportDialog : BaseDialogFragment() {
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
"${appInfo.versionName}-${appInfo.buildFlavor}",
- supportInfo.loginData.baseUrl + "/" + supportInfo.loginData.symbol,
+ supportInfo.loginData.let { "${it.baseUrl}/${it.defaultSymbol}/${it.userEnteredSymbol}" },
preferencesRepository.installationId,
getLastErrorFromStudentSelectScreen(),
dialogLoginSupportSchoolInput.text.takeIf { !it.isNullOrBlank() }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt
index 02bfde5d7..cc88b09e9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt
@@ -60,7 +60,7 @@ class LoginSymbolPresenter @Inject constructor(
}
loginData = loginData.copy(
- symbol = view?.symbolValue?.getNormalizedSymbol(),
+ userEnteredSymbol = view?.symbolValue?.getNormalizedSymbol(),
)
resourceFlow {
studentRepository.getUserSubjectsFromScrapper(
@@ -68,7 +68,7 @@ class LoginSymbolPresenter @Inject constructor(
password = loginData.password,
scrapperBaseUrl = loginData.baseUrl,
domainSuffix = loginData.domainSuffix,
- symbol = loginData.symbol.orEmpty(),
+ symbol = loginData.userEnteredSymbol.orEmpty(),
)
}.onEach { user ->
registerUser = user.dataOrNull
@@ -93,7 +93,7 @@ class LoginSymbolPresenter @Inject constructor(
else -> {
val enteredSymbolDetails = user.data.symbols
.firstOrNull()
- ?.takeIf { it.symbol == loginData.symbol }
+ ?.takeIf { it.symbol == loginData.userEnteredSymbol }
if (enteredSymbolDetails?.error is InvalidSymbolException) {
view?.run {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt
index 6b442d1c2..7e8c876ef 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt
@@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.modules.timetable
import android.os.Handler
import android.os.Looper
-import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
@@ -20,8 +19,8 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
-import io.github.wulkanowy.data.toFirstResult
-import io.github.wulkanowy.data.waitForResult
+import io.github.wulkanowy.domain.timetable.IsStudentHasLessonsOnWeekendUseCase
+import io.github.wulkanowy.domain.timetable.IsWeekendHasLessonsUseCase
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
@@ -31,16 +30,12 @@ import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.isJustFinished
import io.github.wulkanowy.utils.isShowTimeUntil
import io.github.wulkanowy.utils.left
-import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
-import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.until
-import kotlinx.coroutines.flow.firstOrNull
import timber.log.Timber
-import java.time.DayOfWeek
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDate.now
@@ -54,6 +49,8 @@ class TimetablePresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val timetableRepository: TimetableRepository,
+ private val isStudentHasLessonsOnWeekendUseCase: IsStudentHasLessonsOnWeekendUseCase,
+ private val isWeekendHasLessonsUseCase: IsWeekendHasLessonsUseCase,
private val semesterRepository: SemesterRepository,
private val prefRepository: PreferencesRepository,
private val analytics: AnalyticsHelper,
@@ -165,7 +162,7 @@ class TimetablePresenter @Inject constructor(
}
.logResourceStatus("load timetable data")
.onResourceData {
- isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessons(it.lessons)
+ isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessonsUseCase(it.lessons)
view?.run {
enableSwipe(true)
@@ -199,15 +196,7 @@ class TimetablePresenter @Inject constructor(
private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
if (initialDate == null) {
- val lessons = timetableRepository.getTimetable(
- student = student,
- semester = semester,
- start = now().monday,
- end = now().sunday,
- forceRefresh = false,
- timetableType = TimetableRepository.TimetableType.NORMAL
- ).toFirstResult().dataOrNull?.lessons.orEmpty()
- isWeekendHasLessons = isWeekendHasLessons(lessons)
+ isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(student, semester)
initialDate = getInitialDate(semester)
}
@@ -216,15 +205,6 @@ class TimetablePresenter @Inject constructor(
}
}
- private fun isWeekendHasLessons(
- lessons: List,
- ): Boolean = lessons.any {
- it.date.dayOfWeek in listOf(
- DayOfWeek.SATURDAY,
- DayOfWeek.SUNDAY,
- )
- }
-
private fun getInitialDate(semester: Semester): LocalDate {
val now = now()
diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt
index 04f3ba463..012bbd26d 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,6 +1,8 @@
-Wersja 2.3.5
+Wersja 2.4.0
-— naprawiliśmy ładowanie frekwencji dla szkół używających eduOne
-— naprawiliśmy wielokrotne wysyłanie powiadomień o zmianach w planie lekcji
+— naprawiliśmy logowanie do aplikacji na odmianie standardowej
+— naprawiliśmy wyświetlanie lekcji na kolejny dzień w kafelku na ekranie Start
+— dodaliśmy oceny opisowe
+— dodaliśmy kolorowe opisy we frekwencji we wpisach innych niż obecność
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
diff --git a/app/src/main/res/layout/item_grade_summary.xml b/app/src/main/res/layout/item_grade_summary.xml
index 30aa6e77b..2c8c4ea37 100644
--- a/app/src/main/res/layout/item_grade_summary.xml
+++ b/app/src/main/res/layout/item_grade_summary.xml
@@ -64,10 +64,12 @@
+ android:layout_height="1dp"
+ android:id="@+id/gradeSummaryItemPointsDivider"
+ android:background="@drawable/ic_all_divider" />
+ android:id="@+id/gradeSummaryItemPredictedDivider"
+ android:layout_height="1dp"
+ android:background="@drawable/ic_all_divider" />
+ android:layout_height="1dp"
+ android:id="@+id/gradeSummaryItemFinalDivider"
+ android:background="@drawable/ic_all_divider" />
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index b4f1f878a..85a67f9b3 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -117,6 +117,7 @@
Součet bodů
Konečná známka
Předpokládaná známka
+ Popisná známka
Vypočítaný průměr
Jak funguje vypočítaný průměr?
Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů
@@ -160,6 +161,12 @@
- Nové konečné známky
- Nové konečné známky
+
+ - Nová popisná známka
+ - Nové popisné známky
+ - Nové popisné známky
+ - Nové popisné známky
+
- Máte %1$d novou známku
- Máte %1$d nové známky
@@ -178,6 +185,12 @@
- Máte %1$d nových konečných známek
- Máte %1$d nových konečných známek
+
+ - Máte %1$d novou popisnou známku
+ - Máte %1$d nové popisné známky
+ - Máte %1$d nových popisných známek
+ - Máte %1$d nových popisných známek
+
Lekce
Učebna
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 7e0ce8689..0fbba392c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -117,6 +117,7 @@
Gesamtpunkte
Finaler Note
Vorhergesagte Note
+ Descriptive grade
Berechnender Durchschnitt
Wie funktioniert der berechnete Durchschnitt?
Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\nDurchschnitt der Noten nur aus dem ausgewählten Semester :\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Durchschnitte aus beiden Semestern:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Noten aus dem ganzen Jahr:\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Mittelwerte
@@ -152,6 +153,10 @@
- Neue Abschlussnote
- Neue Abschlussnoten
+
+ - New descriptive grade
+ - New descriptive grades
+
- Du hast %1$d Note bekommen
- Du hast %1$d Noten bekommen
@@ -164,6 +169,10 @@
- Sie haben %1$d Abschlussnote bekommen
- Sie haben %1$d Abschlussnoten bekommen
+
+ - You received %1$d descriptive grade
+ - You received %1$d descriptive grades
+
Lektion
Klassenzimmer
diff --git a/app/src/main/res/values-night-v31/styles.xml b/app/src/main/res/values-night-v31/styles.xml
index 6e6c4d79c..808bc714d 100644
--- a/app/src/main/res/values-night-v31/styles.xml
+++ b/app/src/main/res/values-night-v31/styles.xml
@@ -32,6 +32,9 @@
- @color/material_dynamic_primary40
- @color/timetable_canceled_dark
- @color/timetable_change_dark
+ - @color/attendance_absence_dark
+ - @color/attendance_lateness_dark
+
- @color/colorErrorLight
- @color/colorDividerInverse
- @color/material_dynamic_secondary20
diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml
index 5d9aa22a6..5840a051e 100644
--- a/app/src/main/res/values-night/styles.xml
+++ b/app/src/main/res/values-night/styles.xml
@@ -21,6 +21,8 @@
- @color/colorSurfaceDark
- @color/timetable_canceled_dark
- @color/timetable_change_dark
+ - @color/attendance_absence_dark
+ - @color/attendance_lateness_dark
- @color/colorErrorLight
- @color/colorDividerInverse
- @color/colorSwipeRefreshDark
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 1b4fbe664..df9ef8dbd 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -117,6 +117,7 @@
Suma punktów
Ocena końcowa
Przewidywana ocena
+ Ocena opisowa
Obliczona średnia
Jak działa obliczona średnia?
Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich
@@ -160,6 +161,12 @@
- Nowe oceny końcowe
- Nowe oceny końcowe
+
+ - Nowa ocena opisowa
+ - Nowe oceny opisowe
+ - Nowe oceny opisowe
+ - Nowe oceny opisowe
+
- Masz %1$d nową ocenę
- Masz %1$d nowe oceny
@@ -178,6 +185,12 @@
- Masz %1$d nowych końcowych ocen
- Masz %1$d nowych końcowych ocen
+
+ - Masz %1$d nową ocenę opisową
+ - Masz %1$d nowe oceny opisowe
+ - Masz %1$d nowych ocen opisowych
+ - Masz %1$d nowych ocen opisowych
+
Lekcja
Sala
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 2ca669287..fffc5ce1e 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -117,6 +117,7 @@
Сумма баллов
Итоговая оценка
Ожидаемая оценка
+ Descriptive grade
Рассчитанная средняя оценка
Как работает \"Рассчитанная средняя оценка\"?
Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\nСредняя из оценок выбранного семестра:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\nСредняя из средних оценок семестров:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\nСредняя из оценок со всего года:\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n3. Расчет среднего арифметического суммированных чисел
@@ -160,6 +161,12 @@
- Новые итоговые оценки
- Новые итоговые оценки
+
+ - New descriptive grade
+ - New descriptive grades
+ - New descriptive grades
+ - New descriptive grades
+
- Вы получили %1$d новую оценку
- Вы получили %1$d новые оценки
@@ -178,6 +185,12 @@
- Вы получили %1$d новых итоговых оценок
- Вы получили %1$d новых итоговые оценки
+
+ - You received %1$d descriptive grade
+ - You received %1$d descriptive grades
+ - You received %1$d descriptive grades
+ - You received %1$d descriptive grades
+
Урок
Аудитория
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index aaf04bc85..1e822890b 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -117,6 +117,7 @@
Súčet bodov
Konečná známka
Predpokladaná známka
+ Popisná známka
Vypočítaný priemer
Ako funguje vypočítaný priemer?
Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\nPriemer známok iba z vybraného semestra:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer priemerov z oboch semestrov:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer známok z celého roka:\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov
@@ -160,6 +161,12 @@
- Nové konečné známky
- Nové konečné známky
+
+ - Nová popisná známka
+ - Nové popisné známky
+ - Nové popisné známky
+ - Nové popisné známky
+
- Máte %1$d novú známku
- Máte %1$d nové známky
@@ -178,6 +185,12 @@
- Máte %1$d nových konečných známok
- Máte %1$d nových konečných známok
+
+ - Máte %1$d novú popisnú známku
+ - Máte %1$d nové popisné známky
+ - Máte %1$d nových popisných známok
+ - Máte %1$d nových popisných známok
+
Lekcia
Učebňa
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index fffae003b..47034de62 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -97,8 +97,8 @@
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
- Your account password has been changed. You need to log in to Wulkanowy again
- Password changed
+ Пароль вашого облікового запису був змінений. Ви повинні увійти в Wulkanowy знову
+ Пароль змінено
Підтримка додатку
Вам подобається цей додаток? Підтримайте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете відключити в будь-який час
Увімкнути рекламу
@@ -117,6 +117,7 @@
Всього балів
Підсумкова оцінка
Передбачувана оцінка
+ Описова оцінка
Розрахована середня оцінка
Як працює \"Розрахована середня оцінка\"?
Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів. Це дозволяє дізнатися приблизну кінцеву середню оцінку. Вона розраховується спосібом, обраним користувачем у налаштуваннях програми. Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку. Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки і не розраховує їх самостійно. Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх
@@ -160,6 +161,12 @@
- Нові підсумкові оцінки
- Нові підсумкові оцінки
+
+ - Нова описова оцінка
+ - Нових описових оцінок
+ - Описових оцінок
+ - Нові описові оцінки
+
- Ви отримали %1$d нову оцінку
- Ви отримали %1$d нові оцінки
@@ -178,6 +185,12 @@
- Ви отримали %1$d нових підсумкових оцінок
- Ви отримали %1$d нових підсумкових оцінок
+
+ - Ви отримали %1$d описову оцінку
+ - Ви отримали %1$d нові описові оцінки
+ - Ви отримали %1$d нових описових оцінок
+ - Ви отримали %1$d нових описових оцінок
+
Урок
Аудиторія
diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml
index bb47b22ed..358806681 100644
--- a/app/src/main/res/values-v31/styles.xml
+++ b/app/src/main/res/values-v31/styles.xml
@@ -34,6 +34,9 @@
- @color/material_dynamic_primary80
- @color/timetable_canceled_light
- @color/timetable_change_light
+ - @color/attendance_absence_light
+ - @color/attendance_lateness_light
+
- @color/colorError
- @color/colorDivider
- @color/material_dynamic_secondary90
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index 522b6e116..6439b462f 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -44,10 +44,10 @@
- https://vulcan.net.pl/?login
- https://vulcan.net.pl/?login
- https://vulcan.net.pl/?email&customSuffix
- - https://fakelog.cf/?email
+ - https://wulkanowy.net.pl/?email
- - Default
+ - warszawa
- opole
- gdansk
- lublin
@@ -66,7 +66,7 @@
- gminaulanmajorat
- gminaozorkow
- gminalopiennikgorny
- - Default
+ - warszawa
- powiatwulkanowy
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index aa58fa09e..3c3fdb8e9 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -7,4 +7,6 @@
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 87057c61d..8ad27ad88 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -49,6 +49,12 @@
#ff8f00
#ffd54f
+ #d32f2f
+ #e57373
+
+ #cd2a01
+ #f05d0e
+
#1f000000
#1fffffff
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 60d85606d..f1fa3ce73 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -130,6 +130,7 @@
Total points
Final grade
Predicted grade
+ Descriptive grade
Calculated average
How does Calculated Average work?
The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
@@ -165,6 +166,10 @@
- New final grade
- New final grades
+
+ - New descriptive grade
+ - New descriptive grades
+
- You received %1$d grade
- You received %1$d grades
@@ -177,6 +182,10 @@
- You received %1$d final grade
- You received %1$d final grades
+
+ - You received %1$d descriptive grade
+ - You received %1$d descriptive grades
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index a0023dda1..b5b029501 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -19,6 +19,8 @@
- @color/colorSurface
- @color/timetable_canceled_light
- @color/timetable_change_light
+ - @color/attendance_absence_light
+ - @color/attendance_lateness_light
- @color/colorError
- @color/colorDivider
- @color/colorSwipeRefresh
diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
index eac1389f4..9f5d731b6 100644
--- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
+++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
@@ -42,6 +42,7 @@ fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalD
diaryName = "$semesterId",
schoolYear = 1970,
classId = 0,
+ className = "Ti",
semesterNumber = semesterName,
unitId = 1,
start = start,
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
index 5a1877cc0..515b0d66d 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.GradeDao
+import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
@@ -42,6 +43,9 @@ class GradeRepositoryTest {
@MockK
private lateinit var gradeSummaryDb: GradeSummaryDao
+ @MockK
+ private lateinit var gradeDescriptiveDb: GradeDescriptiveDao
+
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
@@ -56,7 +60,8 @@ class GradeRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
- gradeRepository = GradeRepository(gradeDb, gradeSummaryDb, sdk, refreshHelper)
+ gradeRepository =
+ GradeRepository(gradeDb, gradeSummaryDb, gradeDescriptiveDb, sdk, refreshHelper)
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()
@@ -68,6 +73,13 @@ class GradeRepositoryTest {
)
coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
+
+ coEvery { gradeDescriptiveDb.loadAll(any(), any()) } returnsMany listOf(
+ flowOf(listOf()),
+ )
+
+ coEvery { gradeDescriptiveDb.deleteAll(any()) } just Runs
+ coEvery { gradeDescriptiveDb.insertAll(any()) } returns listOf()
}
@Test
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
index 31ea3322b..6a717f6f6 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
@@ -1,12 +1,16 @@
package io.github.wulkanowy.ui.modules.grade
-import io.github.wulkanowy.data.*
+import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
+import io.github.wulkanowy.data.resourceFlow
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.Status
@@ -158,7 +162,9 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { noWeightGrades to noWeightGradesSummary }
+ } returns resourceFlow {
+ Triple(noWeightGrades, noWeightGradesSummary, emptyList())
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -186,7 +192,9 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { noWeightGrades to noWeightGradesArithmeticSummary }
+ } returns resourceFlow {
+ Triple(noWeightGrades, noWeightGradesArithmeticSummary, emptyList())
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -211,8 +219,24 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
emit(Resource.Loading())
- emit(Resource.Intermediate(secondGradeWithModifier to secondSummariesWithModifier))
- emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(
+ Resource.Intermediate(
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ )
+ )
+ emit(
+ Resource.Success(
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ )
+ )
}
val items = runBlocking {
@@ -253,11 +277,27 @@ class GradeAverageProviderTest {
coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flow {
emit(Resource.Loading())
delay(1000)
- emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(
+ Resource.Success(
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ )
+ )
}
coEvery { gradeRepository.getGrades(student, semesters[1], false) } returns flow {
emit(Resource.Loading())
- emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(
+ Resource.Success(
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ )
+ )
}
val items = runBlocking {
@@ -296,7 +336,13 @@ class GradeAverageProviderTest {
semesters[1],
false
)
- } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ } returns resourceFlow {
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ }
coEvery {
gradeRepository.getGrades(
student,
@@ -304,8 +350,10 @@ class GradeAverageProviderTest {
false
)
} returns resourceFlow {
- listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)) to listOf(
- getSummary(semesters[2].semesterId, "Język polski", 2.5)
+ Triple(
+ listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)),
+ listOf(getSummary(semesters[2].semesterId, "Język polski", 2.5)),
+ emptyList()
)
}
@@ -332,7 +380,13 @@ class GradeAverageProviderTest {
semesters[1],
false
)
- } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ } returns resourceFlow {
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ }
coEvery {
gradeRepository.getGrades(
student,
@@ -340,12 +394,14 @@ class GradeAverageProviderTest {
false
)
} returns resourceFlow {
- emptyList() to listOf(
- getSummary(
- 24,
- "Język polski",
- .0
- )
+ Triple(
+ emptyList(), listOf(
+ getSummary(
+ 24,
+ "Język polski",
+ .0
+ )
+ ), emptyList()
)
}
@@ -372,14 +428,22 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { emptyList() to emptyList() }
+ } returns resourceFlow {
+ Triple(
+ emptyList(),
+ emptyList(),
+ emptyList()
+ )
+ }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns resourceFlow { emptyList() to emptyList() }
+ } returns resourceFlow {
+ Triple(emptyList(), emptyList(), emptyList())
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -404,7 +468,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ } returns resourceFlow {
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -438,7 +508,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ } returns resourceFlow {
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -472,7 +548,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ } returns resourceFlow {
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -506,7 +588,13 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ } returns resourceFlow {
+ Triple(
+ secondGradeWithModifier,
+ secondSummariesWithModifier,
+ emptyList()
+ )
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -534,7 +622,7 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { secondGrades to secondSummaries }
+ } returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -564,7 +652,7 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns resourceFlow { secondGrades to secondSummaries }
+ } returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -594,7 +682,7 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to firstSummaries }
+ } returns resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -625,8 +713,8 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
- emit(Resource.Intermediate(firstGrades to firstSummaries))
- emit(Resource.Success(firstGrades to firstSummaries))
+ emit(Resource.Intermediate(Triple(firstGrades, firstSummaries, emptyList())))
+ emit(Resource.Success(Triple(firstGrades, firstSummaries, emptyList())))
}
val items = runBlocking {
@@ -675,9 +763,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- firstGrades to listOf(
- getSummary(22, "Matematyka", 3.0),
- getSummary(22, "Fizyka", 3.5)
+ Triple(
+ firstGrades, listOf(
+ getSummary(22, "Matematyka", 3.0),
+ getSummary(22, "Fizyka", 3.5)
+ ), emptyList()
)
}
coEvery {
@@ -687,9 +777,13 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- secondGrades to listOf(
- getSummary(22, "Matematyka", 3.5),
- getSummary(22, "Fizyka", 4.0)
+ Triple(
+ secondGrades,
+ listOf(
+ getSummary(22, "Matematyka", 3.5),
+ getSummary(22, "Fizyka", 4.0)
+ ),
+ emptyList()
)
}
@@ -723,17 +817,21 @@ class GradeAverageProviderTest {
emit(Resource.Loading())
emit(
Resource.Intermediate(
- firstGrades to listOf(
- getSummary(22, "Matematyka", 3.0),
- getSummary(22, "Fizyka", 3.5)
+ Triple(
+ firstGrades, listOf(
+ getSummary(22, "Matematyka", 3.0),
+ getSummary(22, "Fizyka", 3.5)
+ ), emptyList()
)
)
)
emit(
Resource.Success(
- firstGrades to listOf(
- getSummary(22, "Matematyka", 3.0),
- getSummary(22, "Fizyka", 3.5)
+ Triple(
+ firstGrades, listOf(
+ getSummary(22, "Matematyka", 3.0),
+ getSummary(22, "Fizyka", 3.5)
+ ), emptyList()
)
)
)
@@ -742,17 +840,21 @@ class GradeAverageProviderTest {
emit(Resource.Loading())
emit(
Resource.Intermediate(
- secondGrades to listOf(
- getSummary(22, "Matematyka", 3.5),
- getSummary(22, "Fizyka", 4.0)
+ Triple(
+ secondGrades, listOf(
+ getSummary(22, "Matematyka", 3.5),
+ getSummary(22, "Fizyka", 4.0)
+ ), emptyList()
)
)
)
emit(
Resource.Success(
- secondGrades to listOf(
- getSummary(22, "Matematyka", 3.5),
- getSummary(22, "Fizyka", 4.0)
+ Triple(
+ secondGrades, listOf(
+ getSummary(22, "Matematyka", 3.5),
+ getSummary(22, "Fizyka", 4.0)
+ ), emptyList()
)
)
)
@@ -803,7 +905,7 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to firstSummaries }
+ } returns resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
@@ -811,9 +913,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- secondGrades to listOf(
- getSummary(22, "Matematyka", 1.1),
- getSummary(22, "Fizyka", 7.26)
+ Triple(
+ secondGrades, listOf(
+ getSummary(22, "Matematyka", 1.1),
+ getSummary(22, "Fizyka", 7.26)
+ ), emptyList()
)
}
@@ -850,9 +954,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- firstGrades to listOf(
- getSummary(22, "Matematyka", .0),
- getSummary(22, "Fizyka", .0)
+ Triple(
+ firstGrades, listOf(
+ getSummary(22, "Matematyka", .0),
+ getSummary(22, "Fizyka", .0)
+ ), emptyList()
)
}
coEvery {
@@ -862,9 +968,11 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- secondGrades to listOf(
- getSummary(22, "Matematyka", .0),
- getSummary(22, "Fizyka", .0)
+ Triple(
+ secondGrades, listOf(
+ getSummary(22, "Matematyka", .0),
+ getSummary(22, "Fizyka", .0)
+ ), emptyList()
)
}
@@ -889,24 +997,28 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
emit(Resource.Loading())
- emit(Resource.Intermediate(firstGrades to firstSummaries))
- emit(Resource.Success(firstGrades to firstSummaries))
+ emit(Resource.Intermediate(Triple(firstGrades, firstSummaries, emptyList())))
+ emit(Resource.Success(Triple(firstGrades, firstSummaries, emptyList())))
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
emit(Resource.Loading())
emit(
Resource.Intermediate(
- secondGrades to listOf(
- getSummary(22, "Matematyka", 1.1),
- getSummary(22, "Fizyka", 7.26)
+ Triple(
+ secondGrades, listOf(
+ getSummary(22, "Matematyka", 1.1),
+ getSummary(22, "Fizyka", 7.26)
+ ), emptyList()
)
)
)
emit(
Resource.Success(
- secondGrades to listOf(
- getSummary(22, "Matematyka", 1.1),
- getSummary(22, "Fizyka", 7.26)
+ Triple(
+ secondGrades, listOf(
+ getSummary(22, "Matematyka", 1.1),
+ getSummary(22, "Fizyka", 7.26)
+ ), emptyList()
)
)
)
@@ -958,14 +1070,14 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to emptyList() }
+ } returns resourceFlow { Triple(firstGrades, emptyList(), emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns resourceFlow { secondGrades to emptyList() }
+ } returns resourceFlow { Triple(secondGrades, emptyList(), emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -1000,14 +1112,14 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to emptyList() }
+ } returns resourceFlow { Triple(firstGrades, emptyList(), emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns resourceFlow { secondGrades to emptyList() }
+ } returns resourceFlow { Triple(secondGrades, emptyList(), emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -1043,8 +1155,10 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- firstGrades to listOf(
- getSummary(22, "Matematyka", 4.0)
+ Triple(
+ firstGrades, listOf(
+ getSummary(22, "Matematyka", 4.0)
+ ), emptyList()
)
}
coEvery {
@@ -1054,8 +1168,10 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- secondGrades to listOf(
- getSummary(23, "Matematyka", 3.0)
+ Triple(
+ secondGrades, listOf(
+ getSummary(23, "Matematyka", 3.0)
+ ), emptyList()
)
}
@@ -1092,14 +1208,20 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to firstSummaries }
+ } returns resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns resourceFlow { secondGrades to secondSummaries.dropLast(1) }
+ } returns resourceFlow {
+ Triple(
+ secondGrades,
+ secondSummaries.dropLast(1),
+ emptyList()
+ )
+ }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -1134,14 +1256,20 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to firstSummaries.dropLast(1) }
+ } returns resourceFlow {
+ Triple(
+ firstGrades,
+ firstSummaries.dropLast(1),
+ emptyList()
+ )
+ }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns resourceFlow { secondGrades to secondSummaries }
+ } returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -1176,14 +1304,20 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns resourceFlow { firstGrades to firstSummaries.dropLast(1) }
+ } returns resourceFlow {
+ Triple(
+ firstGrades,
+ firstSummaries.dropLast(1),
+ emptyList()
+ )
+ }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns resourceFlow { secondGrades to secondSummaries }
+ } returns resourceFlow { Triple(secondGrades, secondSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -1219,16 +1353,20 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- listOf(
- getGrade(22, "Fizyka", 5.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 5.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0)
- ) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(22, "Fizyka", 5.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 5.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
coEvery {
gradeRepository.getGrades(
@@ -1237,11 +1375,15 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- listOf(
- getGrade(23, "Fizyka", 5.0, weight = 1.0),
- getGrade(23, "Fizyka", 5.0, weight = 2.0),
- getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
- ) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(23, "Fizyka", 5.0, weight = 1.0),
+ getGrade(23, "Fizyka", 5.0, weight = 2.0),
+ getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
val items = runBlocking {
@@ -1266,23 +1408,31 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.ALL_YEAR)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
- listOf(
- getGrade(22, "Fizyka", 5.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 5.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0)
- ) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(22, "Fizyka", 5.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 5.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
- listOf(
- getGrade(23, "Fizyka", 5.0, weight = 1.0),
- getGrade(23, "Fizyka", 5.0, weight = 2.0),
- getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
- ) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(23, "Fizyka", 5.0, weight = 1.0),
+ getGrade(23, "Fizyka", 5.0, weight = 2.0),
+ getGrade(23, "Fizyka", 4.0, modifier = 0.3, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
val items = runBlocking {
@@ -1313,23 +1463,31 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
- listOf(
- getGrade(22, "Fizyka", 5.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 5.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0)
- ) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(22, "Fizyka", 5.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 5.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
- listOf(
- getGrade(23, "Fizyka", 5.0, weight = 1.0),
- getGrade(23, "Fizyka", 5.0, weight = 2.0),
- getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
- ) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(23, "Fizyka", 5.0, weight = 1.0),
+ getGrade(23, "Fizyka", 5.0, weight = 2.0),
+ getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
val items = runBlocking {
@@ -1366,16 +1524,20 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- listOf(
- getGrade(22, "Fizyka", 5.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 5.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 4.0),
- getGrade(22, "Fizyka", 6.0, weight = 2.0)
- ) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(22, "Fizyka", 5.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 5.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 4.0),
+ getGrade(22, "Fizyka", 6.0, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
coEvery {
gradeRepository.getGrades(
@@ -1384,11 +1546,15 @@ class GradeAverageProviderTest {
true
)
} returns resourceFlow {
- listOf(
- getGrade(23, "Fizyka", 5.0, weight = 1.0),
- getGrade(23, "Fizyka", 5.0, weight = 2.0),
- getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
- ) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
+ Triple(
+ listOf(
+ getGrade(23, "Fizyka", 5.0, weight = 1.0),
+ getGrade(23, "Fizyka", 5.0, weight = 2.0),
+ getGrade(23, "Fizyka", 4.0, modifier = 0.33, weight = 2.0)
+ ),
+ listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0)),
+ emptyList()
+ )
}
val items = runBlocking {
@@ -1413,9 +1579,9 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(false)
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns
- resourceFlow { firstGrades to firstSummaries }
+ resourceFlow { Triple(firstGrades, firstSummaries, emptyList()) }
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns
- resourceFlow { listOf() to firstSummaries }
+ resourceFlow { Triple(listOf(), firstSummaries, emptyList()) }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
index fad6436d8..34965f00d 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
@@ -58,7 +58,7 @@ class LoginStudentSelectPresenterTest {
login = "",
password = "",
baseUrl = "",
- symbol = null,
+ defaultSymbol = "warszawa",
domainSuffix = "",
)
diff --git a/build.gradle b/build.gradle
index 095d1b72f..e6e090b80 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:$kotlin_version-1.0.16"
- classpath 'com.android.tools.build:gradle:8.2.1'
+ classpath 'com.android.tools.build:gradle:8.2.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.4.0'
classpath 'com.huawei.agconnect:agcp:1.9.1.303'