forked from github/wulkanowy-mirror
Add points to class grades statistics (#512)
This commit is contained in:
parent
d411d86355
commit
f9474af39e
@ -116,7 +116,7 @@ jobs:
|
|||||||
adb shell input keyevent 82
|
adb shell input keyevent 82
|
||||||
- run:
|
- run:
|
||||||
name: Run instrumented tests
|
name: Run instrumented tests
|
||||||
command: ./gradlew clean createPlayDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
|
command: ./gradlew clean createFdroidDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
|
||||||
- run:
|
- run:
|
||||||
name: Collect logs from emulator
|
name: Collect logs from emulator
|
||||||
command: adb logcat -d > ./app/build/reports/logcat_emulator.txt
|
command: adb logcat -d > ./app/build/reports/logcat_emulator.txt
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -110,4 +110,5 @@ Thumbs.db
|
|||||||
|
|
||||||
### AndroidStudio Patch ###
|
### AndroidStudio Patch ###
|
||||||
|
|
||||||
!/gradle/wrapper/gradle-wrapper.jar
|
!/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
@ -50,7 +50,7 @@ script:
|
|||||||
- fossa --no-ansi || true
|
- fossa --no-ansi || true
|
||||||
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
|
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
|
||||||
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
||||||
- ./gradlew createPlayDebugCoverageReport --stacktrace --daemon
|
- ./gradlew createFdroidDebugCoverageReport --stacktrace --daemon
|
||||||
- ./gradlew jacocoTestReport --stacktrace --daemon
|
- ./gradlew jacocoTestReport --stacktrace --daemon
|
||||||
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
|
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
|
||||||
git fetch --unshallow;
|
git fetch --unshallow;
|
||||||
|
@ -123,7 +123,7 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:api:23ae9f6"
|
implementation "io.github.wulkanowy:api:b7cbd9a"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "androidx.core:core-ktx:1.1.0"
|
implementation "androidx.core:core-ktx:1.1.0"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 16,
|
"version": 16,
|
||||||
"identityHash": "92490bb628715605f7a6776d0e730eae",
|
"identityHash": "1eccdcb09adc922713ef67f298ec77a7",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "Students",
|
"tableName": "Students",
|
||||||
@ -838,6 +838,56 @@
|
|||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"tableName": "GradesPointsStatistics",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"tableName": "Messages",
|
"tableName": "Messages",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)",
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)",
|
||||||
@ -1419,62 +1469,12 @@
|
|||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
},
|
|
||||||
{
|
|
||||||
"tableName": "Teachers",
|
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldPath": "id",
|
|
||||||
"columnName": "id",
|
|
||||||
"affinity": "INTEGER",
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"primaryKey": {
|
|
||||||
"columnNames": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"autoGenerate": true
|
|
||||||
},
|
|
||||||
"indices": [],
|
|
||||||
"foreignKeys": []
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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, '92490bb628715605f7a6776d0e730eae')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1eccdcb09adc922713ef67f298ec77a7')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
1530
app/schemas/io.github.wulkanowy.data.db.AppDatabase/17.json
Normal file
1530
app/schemas/io.github.wulkanowy.data.db.AppDatabase/17.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,8 @@ abstract class AbstractMigrationTest {
|
|||||||
Migration13(),
|
Migration13(),
|
||||||
Migration14(),
|
Migration14(),
|
||||||
Migration15(),
|
Migration15(),
|
||||||
Migration16()
|
Migration16(),
|
||||||
|
Migration17()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
// close the database and release any stream resources when the test finishes
|
// close the database and release any stream resources when the test finishes
|
||||||
|
@ -4,6 +4,7 @@ import androidx.room.Room
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import io.github.wulkanowy.data.db.AppDatabase
|
import io.github.wulkanowy.data.db.AppDatabase
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -24,7 +25,7 @@ class GradeStatisticsLocalTest {
|
|||||||
fun createDb() {
|
fun createDb() {
|
||||||
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
|
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
|
||||||
.build()
|
.build()
|
||||||
gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics)
|
gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics, testDb.gradePointsStatistics)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -63,7 +64,52 @@ class GradeStatisticsLocalTest {
|
|||||||
assertEquals(stats[0].subject, "Wszystkie")
|
assertEquals(stats[0].subject, "Wszystkie")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveAndRead_points() {
|
||||||
|
gradeStatisticsLocal.saveGradesPointsStatistics(listOf(
|
||||||
|
getGradePointsStatistics("Matematyka", 2, 1),
|
||||||
|
getGradePointsStatistics("Chemia", 2, 1),
|
||||||
|
getGradePointsStatistics("Fizyka", 1, 2)
|
||||||
|
))
|
||||||
|
|
||||||
|
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||||
|
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||||
|
"Matematyka"
|
||||||
|
).blockingGet()
|
||||||
|
with(stats) {
|
||||||
|
assertEquals(subject, "Matematyka")
|
||||||
|
assertEquals(others, 5.0)
|
||||||
|
assertEquals(student, 5.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveAndRead_subjectEmpty() {
|
||||||
|
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||||
|
|
||||||
|
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||||
|
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||||
|
"Matematyka"
|
||||||
|
).blockingGet()
|
||||||
|
assertEquals(null, stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveAndRead_allEmpty() {
|
||||||
|
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||||
|
|
||||||
|
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||||
|
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||||
|
"Wszystkie"
|
||||||
|
).blockingGet()
|
||||||
|
assertEquals(null, stats)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
|
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
|
||||||
return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
|
return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getGradePointsStatistics(subject: String, studentId: Int, semesterId: Int): GradePointsStatistics {
|
||||||
|
return GradePointsStatistics(studentId, semesterId, subject, 5.0, 5.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,10 @@ internal class RepositoryModule {
|
|||||||
@Provides
|
@Provides
|
||||||
fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics
|
fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatistics
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideMessagesDao(database: AppDatabase) = database.messagesDao
|
fun provideMessagesDao(database: AppDatabase) = database.messagesDao
|
||||||
|
@ -11,6 +11,7 @@ import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
|||||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||||
@ -30,6 +31,7 @@ import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
|||||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
@ -51,6 +53,7 @@ import io.github.wulkanowy.data.db.migrations.Migration13
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration14
|
import io.github.wulkanowy.data.db.migrations.Migration14
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration15
|
import io.github.wulkanowy.data.db.migrations.Migration15
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration16
|
import io.github.wulkanowy.data.db.migrations.Migration16
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration17
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
@ -73,6 +76,7 @@ import javax.inject.Singleton
|
|||||||
Grade::class,
|
Grade::class,
|
||||||
GradeSummary::class,
|
GradeSummary::class,
|
||||||
GradeStatistics::class,
|
GradeStatistics::class,
|
||||||
|
GradePointsStatistics::class,
|
||||||
Message::class,
|
Message::class,
|
||||||
Note::class,
|
Note::class,
|
||||||
Homework::class,
|
Homework::class,
|
||||||
@ -91,7 +95,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 16
|
const val VERSION_SCHEMA = 17
|
||||||
|
|
||||||
fun newInstance(context: Context): AppDatabase {
|
fun newInstance(context: Context): AppDatabase {
|
||||||
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
|
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
|
||||||
@ -113,7 +117,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration13(),
|
Migration13(),
|
||||||
Migration14(),
|
Migration14(),
|
||||||
Migration15(),
|
Migration15(),
|
||||||
Migration16()
|
Migration16(),
|
||||||
|
Migration17()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@ -137,6 +142,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
|
|
||||||
abstract val gradeStatistics: GradeStatisticsDao
|
abstract val gradeStatistics: GradeStatisticsDao
|
||||||
|
|
||||||
|
abstract val gradePointsStatistics: GradePointsStatisticsDao
|
||||||
|
|
||||||
abstract val messagesDao: MessagesDao
|
abstract val messagesDao: MessagesDao
|
||||||
|
|
||||||
abstract val noteDao: NoteDao
|
abstract val noteDao: NoteDao
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
|
import io.reactivex.Maybe
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Dao
|
||||||
|
interface GradePointsStatisticsDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertAll(gradesStatistics: List<GradePointsStatistics>)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun deleteAll(gradesStatistics: List<GradePointsStatistics>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
|
||||||
|
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<GradePointsStatistics>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
|
||||||
|
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>>
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "GradesPointsStatistics")
|
||||||
|
data class GradePointsStatistics(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "student_id")
|
||||||
|
val studentId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "semester_id")
|
||||||
|
val semesterId: Int,
|
||||||
|
|
||||||
|
val subject: String,
|
||||||
|
|
||||||
|
val others: Double,
|
||||||
|
|
||||||
|
val student: Double
|
||||||
|
) {
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration17 : Migration(16, 17) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("""
|
||||||
|
CREATE TABLE IF NOT EXISTS GradesPointsStatistics(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
student_id INTEGER NOT NULL,
|
||||||
|
semester_id INTEGER NOT NULL,
|
||||||
|
subject TEXT NOT NULL,
|
||||||
|
others REAL NOT NULL,
|
||||||
|
student REAL NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package io.github.wulkanowy.data.repositories.gradestatistics
|
package io.github.wulkanowy.data.repositories.gradestatistics
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
@ -8,27 +10,57 @@ import javax.inject.Inject
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) {
|
class GradeStatisticsLocal @Inject constructor(
|
||||||
|
private val gradeStatisticsDb: GradeStatisticsDao,
|
||||||
|
private val gradePointsStatisticsDb: GradePointsStatisticsDao
|
||||||
|
) {
|
||||||
|
|
||||||
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe<List<GradeStatistics>> {
|
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe<List<GradeStatistics>> {
|
||||||
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
|
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).filter { it.isNotEmpty() }
|
||||||
.filter { !it.isEmpty() }
|
}
|
||||||
|
|
||||||
|
fun getGradesPointsStatistics(semester: Semester): Maybe<List<GradePointsStatistics>> {
|
||||||
|
return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe<List<GradeStatistics>> {
|
fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe<List<GradeStatistics>> {
|
||||||
return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list ->
|
return when (subjectName) {
|
||||||
list.groupBy { it.grade }.map {
|
"Wszystkie" -> gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list ->
|
||||||
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, it.value.fold(0) { acc, e -> acc + e.amount }, false)
|
list.groupBy { it.grade }.map {
|
||||||
|
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
|
||||||
|
it.value.fold(0) { acc, e -> acc + e.amount }, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
|
||||||
|
}.filter { it.isNotEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<GradePointsStatistics> {
|
||||||
|
return when (subjectName) {
|
||||||
|
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
|
||||||
|
if (list.isEmpty()) return@flatMap Maybe.empty<GradePointsStatistics>()
|
||||||
|
Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName,
|
||||||
|
list.fold(.0) { acc, e -> acc + e.others },
|
||||||
|
list.fold(.0) { acc, e -> acc + e.student })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
|
||||||
}
|
}
|
||||||
else gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)).filter { !it.isEmpty() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
||||||
gradeStatisticsDb.insertAll(gradesStatistics)
|
gradeStatisticsDb.insertAll(gradesStatistics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveGradesPointsStatistics(gradePointsStatistics: List<GradePointsStatistics>) {
|
||||||
|
gradePointsStatisticsDb.insertAll(gradePointsStatistics)
|
||||||
|
}
|
||||||
|
|
||||||
fun deleteGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
fun deleteGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
||||||
gradeStatisticsDb.deleteAll(gradesStatistics)
|
gradeStatisticsDb.deleteAll(gradesStatistics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteGradesPointsStatistics(gradesPointsStatistics: List<GradePointsStatistics>) {
|
||||||
|
gradePointsStatisticsDb.deleteAll(gradesPointsStatistics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.repositories.gradestatistics
|
package io.github.wulkanowy.data.repositories.gradestatistics
|
||||||
|
|
||||||
import io.github.wulkanowy.api.Api
|
import io.github.wulkanowy.api.Api
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
@ -12,7 +13,10 @@ class GradeStatisticsRemote @Inject constructor(private val api: Api) {
|
|||||||
|
|
||||||
fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> {
|
fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> {
|
||||||
return Single.just(api.apply { diaryId = semester.diaryId })
|
return Single.just(api.apply { diaryId = semester.diaryId })
|
||||||
.flatMap { it.getGradesStatistics(semester.semesterId, isSemester) }
|
.flatMap {
|
||||||
|
if (isSemester) it.getGradesAnnualStatistics(semester.semesterId)
|
||||||
|
else it.getGradesPartialStatistics(semester.semesterId)
|
||||||
|
}
|
||||||
.map { gradeStatistics ->
|
.map { gradeStatistics ->
|
||||||
gradeStatistics.map {
|
gradeStatistics.map {
|
||||||
GradeStatistics(
|
GradeStatistics(
|
||||||
@ -26,4 +30,20 @@ class GradeStatisticsRemote @Inject constructor(private val api: Api) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getGradePointsStatistics(semester: Semester): Single<List<GradePointsStatistics>> {
|
||||||
|
return Single.just(api.apply { diaryId = semester.diaryId })
|
||||||
|
.flatMap { it.getGradesPointsStatistics(semester.semesterId) }
|
||||||
|
.map { gradePointsStatistics ->
|
||||||
|
gradePointsStatistics.map {
|
||||||
|
GradePointsStatistics(
|
||||||
|
semesterId = semester.semesterId,
|
||||||
|
studentId = semester.studentId,
|
||||||
|
subject = it.subject,
|
||||||
|
others = it.others,
|
||||||
|
student = it.student
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,11 @@ package io.github.wulkanowy.data.repositories.gradestatistics
|
|||||||
|
|
||||||
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -31,4 +33,19 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
|
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Maybe<GradePointsStatistics> {
|
||||||
|
return local.getGradesPointsStatistics(semester, subjectName).filter { !forceRefresh }
|
||||||
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
|
.flatMapMaybe {
|
||||||
|
if (it) remote.getGradePointsStatistics(semester).toMaybe()
|
||||||
|
else Maybe.error(UnknownHostException())
|
||||||
|
}.flatMap { new ->
|
||||||
|
local.getGradesPointsStatistics(semester).defaultIfEmpty(emptyList())
|
||||||
|
.doOnSuccess { old ->
|
||||||
|
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
|
||||||
|
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
|
||||||
|
}
|
||||||
|
}.flatMap { local.getGradesPointsStatistics(semester, subjectName) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade.statistics
|
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import android.graphics.Color.WHITE
|
import android.graphics.Color.WHITE
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -10,11 +11,15 @@ import android.widget.TextView
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.github.mikephil.charting.components.Legend
|
import com.github.mikephil.charting.components.Legend
|
||||||
import com.github.mikephil.charting.components.LegendEntry
|
import com.github.mikephil.charting.components.LegendEntry
|
||||||
|
import com.github.mikephil.charting.data.BarData
|
||||||
|
import com.github.mikephil.charting.data.BarDataSet
|
||||||
|
import com.github.mikephil.charting.data.BarEntry
|
||||||
import com.github.mikephil.charting.data.PieData
|
import com.github.mikephil.charting.data.PieData
|
||||||
import com.github.mikephil.charting.data.PieDataSet
|
import com.github.mikephil.charting.data.PieDataSet
|
||||||
import com.github.mikephil.charting.data.PieEntry
|
import com.github.mikephil.charting.data.PieEntry
|
||||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
@ -38,7 +43,9 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
fun newInstance() = GradeStatisticsFragment()
|
fun newInstance() = GradeStatisticsFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isViewEmpty get() = gradeStatisticsChart.isEmpty
|
override val isPieViewEmpty get() = gradeStatisticsChart.isEmpty
|
||||||
|
|
||||||
|
override val isBarViewEmpty get() = gradeStatisticsChartPoints.isEmpty
|
||||||
|
|
||||||
private lateinit var gradeColors: List<Pair<Int, Int>>
|
private lateinit var gradeColors: List<Pair<Int, Int>>
|
||||||
|
|
||||||
@ -60,6 +67,11 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
1 to R.color.grade_material_one
|
1 to R.color.grade_material_one
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val gradePointsColors = listOf(
|
||||||
|
Color.parseColor("#37c69c"),
|
||||||
|
Color.parseColor("#d8b12a")
|
||||||
|
)
|
||||||
|
|
||||||
private val gradeLabels = listOf(
|
private val gradeLabels = listOf(
|
||||||
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
|
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
|
||||||
)
|
)
|
||||||
@ -70,8 +82,8 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
messageContainer = gradeStatisticsChart
|
messageContainer = gradeStatisticsSwipe
|
||||||
presenter.onAttachView(this, savedInstanceState?.getBoolean(SAVED_CHART_TYPE))
|
presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? ViewType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
@ -84,6 +96,13 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
with(gradeStatisticsChartPoints) {
|
||||||
|
description.isEnabled = false
|
||||||
|
|
||||||
|
animateXY(1000, 1000)
|
||||||
|
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
|
}
|
||||||
|
|
||||||
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
|
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
|
||||||
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
|
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
|
||||||
|
|
||||||
@ -105,23 +124,25 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(items: List<GradeStatistics>, theme: String) {
|
override fun updatePieData(items: List<GradeStatistics>, theme: String) {
|
||||||
gradeColors = when (theme) {
|
gradeColors = when (theme) {
|
||||||
"vulcan" -> vulcanGradeColors
|
"vulcan" -> vulcanGradeColors
|
||||||
else -> materialGradeColors
|
else -> materialGradeColors
|
||||||
}
|
}
|
||||||
|
|
||||||
gradeStatisticsChart.run {
|
val dataset = PieDataSet(items.map {
|
||||||
data = PieData(PieDataSet(items.map {
|
PieEntry(it.amount.toFloat(), it.grade.toString())
|
||||||
PieEntry(it.amount.toFloat(), it.grade.toString())
|
}, "Legenda").apply {
|
||||||
}, "Legenda").apply {
|
valueTextSize = 12f
|
||||||
valueTextSize = 12f
|
sliceSpace = 1f
|
||||||
sliceSpace = 1f
|
valueTextColor = WHITE
|
||||||
valueTextColor = WHITE
|
setColors(items.map {
|
||||||
setColors(items.map {
|
gradeColors.single { color -> color.first == it.grade }.second
|
||||||
gradeColors.single { color -> color.first == it.grade }.second
|
}.toIntArray(), context)
|
||||||
}.toIntArray(), context)
|
}
|
||||||
}).apply {
|
|
||||||
|
with(gradeStatisticsChart) {
|
||||||
|
data = PieData(dataset).apply {
|
||||||
setTouchEnabled(false)
|
setTouchEnabled(false)
|
||||||
setValueFormatter(object : ValueFormatter() {
|
setValueFormatter(object : ValueFormatter() {
|
||||||
override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
|
override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
|
||||||
@ -144,6 +165,47 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateBarData(item: GradePointsStatistics) {
|
||||||
|
val dataset = BarDataSet(listOf(
|
||||||
|
BarEntry(1f, item.others.toFloat()),
|
||||||
|
BarEntry(2f, item.student.toFloat())
|
||||||
|
), "Legenda").apply {
|
||||||
|
valueTextSize = 12f
|
||||||
|
valueTextColor = requireContext().getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||||
|
valueFormatter = object : ValueFormatter() {
|
||||||
|
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
|
||||||
|
}
|
||||||
|
colors = gradePointsColors
|
||||||
|
}
|
||||||
|
|
||||||
|
with(gradeStatisticsChartPoints) {
|
||||||
|
data = BarData(dataset).apply {
|
||||||
|
barWidth = 0.5f
|
||||||
|
setFitBars(true)
|
||||||
|
}
|
||||||
|
setTouchEnabled(false)
|
||||||
|
xAxis.setDrawLabels(false)
|
||||||
|
xAxis.setDrawGridLines(false)
|
||||||
|
requireContext().getThemeAttrColor(android.R.attr.textColorPrimary).let {
|
||||||
|
axisLeft.textColor = it
|
||||||
|
axisRight.textColor = it
|
||||||
|
}
|
||||||
|
legend.setCustom(listOf(
|
||||||
|
LegendEntry().apply {
|
||||||
|
label = "Średnia klasy"
|
||||||
|
formColor = gradePointsColors[0]
|
||||||
|
form = Legend.LegendForm.SQUARE
|
||||||
|
},
|
||||||
|
LegendEntry().apply {
|
||||||
|
label = "Uczeń"
|
||||||
|
formColor = gradePointsColors[1]
|
||||||
|
form = Legend.LegendForm.SQUARE
|
||||||
|
}
|
||||||
|
))
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun showSubjects(show: Boolean) {
|
override fun showSubjects(show: Boolean) {
|
||||||
gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||||
gradeStatisticsTypeSwitch.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
gradeStatisticsTypeSwitch.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||||
@ -151,12 +213,17 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
|
|
||||||
override fun clearView() {
|
override fun clearView() {
|
||||||
gradeStatisticsChart.clear()
|
gradeStatisticsChart.clear()
|
||||||
|
gradeStatisticsChartPoints.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showContent(show: Boolean) {
|
override fun showPieContent(show: Boolean) {
|
||||||
gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE
|
gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showBarContent(show: Boolean) {
|
||||||
|
gradeStatisticsChartPoints.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
override fun showEmpty(show: Boolean) {
|
override fun showEmpty(show: Boolean) {
|
||||||
gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||||
}
|
}
|
||||||
@ -196,13 +263,17 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
|
gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
|
||||||
presenter.onTypeChange(checkedId == R.id.gradeStatisticsTypeSemester)
|
presenter.onTypeChange(when (checkedId) {
|
||||||
|
R.id.gradeStatisticsTypeSemester -> ViewType.SEMESTER
|
||||||
|
R.id.gradeStatisticsTypePartial -> ViewType.PARTIAL
|
||||||
|
else -> ViewType.POINTS
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putBoolean(SAVED_CHART_TYPE, presenter.currentIsSemester)
|
outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -30,19 +30,19 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
|
|
||||||
private var currentSubjectName: String = "Wszystkie"
|
private var currentSubjectName: String = "Wszystkie"
|
||||||
|
|
||||||
var currentIsSemester = false
|
var currentType: ViewType = ViewType.PARTIAL
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun onAttachView(view: GradeStatisticsView, isSemester: Boolean?) {
|
fun onAttachView(view: GradeStatisticsView, type: ViewType?) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
currentIsSemester = isSemester ?: false
|
currentType = type ?: ViewType.PARTIAL
|
||||||
view.initView()
|
view.initView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
|
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
|
||||||
currentSemesterId = semesterId
|
currentSemesterId = semesterId
|
||||||
loadSubjects()
|
loadSubjects()
|
||||||
loadData(semesterId, currentSubjectName, currentIsSemester, forceRefresh)
|
loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentViewChangeSemester() {
|
fun onParentViewChangeSemester() {
|
||||||
@ -50,7 +50,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
showProgress(true)
|
showProgress(true)
|
||||||
enableSwipe(false)
|
enableSwipe(false)
|
||||||
showRefresh(false)
|
showRefresh(false)
|
||||||
showContent(false)
|
showBarContent(false)
|
||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
clearView()
|
clearView()
|
||||||
}
|
}
|
||||||
@ -65,28 +65,30 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
fun onSubjectSelected(name: String?) {
|
fun onSubjectSelected(name: String?) {
|
||||||
Timber.i("Select grade stats subject $name")
|
Timber.i("Select grade stats subject $name")
|
||||||
view?.run {
|
view?.run {
|
||||||
showContent(false)
|
showBarContent(false)
|
||||||
|
showPieContent(false)
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
enableSwipe(false)
|
enableSwipe(false)
|
||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
clearView()
|
clearView()
|
||||||
}
|
}
|
||||||
(subjects.singleOrNull { it.name == name }?.name)?.let {
|
(subjects.singleOrNull { it.name == name }?.name)?.let {
|
||||||
if (it != currentSubjectName) loadData(currentSemesterId, it, currentIsSemester)
|
if (it != currentSubjectName) loadDataByType(currentSemesterId, it, currentType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onTypeChange(isSemester: Boolean) {
|
fun onTypeChange(type: ViewType) {
|
||||||
Timber.i("Select grade stats semester: $isSemester")
|
Timber.i("Select grade stats semester: $type")
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
view?.run {
|
view?.run {
|
||||||
showContent(false)
|
showBarContent(false)
|
||||||
|
showPieContent(false)
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
enableSwipe(false)
|
enableSwipe(false)
|
||||||
showEmpty(false)
|
showEmpty(false)
|
||||||
clearView()
|
clearView()
|
||||||
}
|
}
|
||||||
loadData(currentSemesterId, currentSubjectName, isSemester)
|
loadDataByType(currentSemesterId, currentSubjectName, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadSubjects() {
|
private fun loadSubjects() {
|
||||||
@ -111,10 +113,18 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
|
||||||
|
currentSubjectName = subjectName
|
||||||
|
currentType = type
|
||||||
|
when (type) {
|
||||||
|
ViewType.SEMESTER -> loadData(semesterId, subjectName, true, forceRefresh)
|
||||||
|
ViewType.PARTIAL -> loadData(semesterId, subjectName, false, forceRefresh)
|
||||||
|
ViewType.POINTS -> loadPointsData(semesterId, subjectName, forceRefresh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) {
|
private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) {
|
||||||
Timber.i("Loading grade stats data started")
|
Timber.i("Loading grade stats data started")
|
||||||
currentSubjectName = subjectName
|
|
||||||
currentIsSemester = isSemester
|
|
||||||
disposable.add(studentRepository.getCurrentStudent()
|
disposable.add(studentRepository.getCurrentStudent()
|
||||||
.flatMap { semesterRepository.getSemesters(it) }
|
.flatMap { semesterRepository.getSemesters(it) }
|
||||||
.flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) }
|
.flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) }
|
||||||
@ -134,14 +144,53 @@ class GradeStatisticsPresenter @Inject constructor(
|
|||||||
Timber.i("Loading grade stats result: Success")
|
Timber.i("Loading grade stats result: Success")
|
||||||
view?.run {
|
view?.run {
|
||||||
showEmpty(it.isEmpty())
|
showEmpty(it.isEmpty())
|
||||||
showContent(it.isNotEmpty())
|
showBarContent(false)
|
||||||
updateData(it, preferencesRepository.gradeColorTheme)
|
showPieContent(it.isNotEmpty())
|
||||||
|
updatePieData(it, preferencesRepository.gradeColorTheme)
|
||||||
}
|
}
|
||||||
analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh)
|
analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh)
|
||||||
}) {
|
}) {
|
||||||
Timber.e("Loading grade stats result: An exception occurred")
|
Timber.e("Loading grade stats result: An exception occurred")
|
||||||
view?.run { showEmpty(isViewEmpty) }
|
view?.run { showEmpty(isPieViewEmpty) }
|
||||||
errorHandler.dispatch(it)
|
errorHandler.dispatch(it)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadPointsData(semesterId: Int, subjectName: String, forceRefresh: Boolean = false) {
|
||||||
|
Timber.i("Loading grade points stats data started")
|
||||||
|
disposable.add(studentRepository.getCurrentStudent()
|
||||||
|
.flatMap { semesterRepository.getSemesters(it) }
|
||||||
|
.flatMapMaybe { gradeStatisticsRepository.getGradesPointsStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, forceRefresh) }
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.doFinally {
|
||||||
|
view?.run {
|
||||||
|
showRefresh(false)
|
||||||
|
showProgress(false)
|
||||||
|
enableSwipe(true)
|
||||||
|
notifyParentDataLoaded(semesterId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
Timber.i("Loading grade points stats result: Success")
|
||||||
|
view?.run {
|
||||||
|
showEmpty(false)
|
||||||
|
showPieContent(false)
|
||||||
|
showBarContent(true)
|
||||||
|
updateBarData(it)
|
||||||
|
}
|
||||||
|
analytics.logEvent("load_grade_points_statistics", "force_refresh" to forceRefresh)
|
||||||
|
}, {
|
||||||
|
Timber.e("Loading grade points stats result: An exception occurred")
|
||||||
|
view?.run { showEmpty(isBarViewEmpty) }
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}, {
|
||||||
|
Timber.d("Loading grade points stats result: No point stats found")
|
||||||
|
view?.run {
|
||||||
|
showBarContent(false)
|
||||||
|
showEmpty(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade.statistics
|
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
interface GradeStatisticsView : BaseView {
|
interface GradeStatisticsView : BaseView {
|
||||||
|
|
||||||
val isViewEmpty: Boolean
|
val isPieViewEmpty: Boolean
|
||||||
|
|
||||||
|
val isBarViewEmpty: Boolean
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateSubjects(data: ArrayList<String>)
|
fun updateSubjects(data: ArrayList<String>)
|
||||||
|
|
||||||
fun updateData(items: List<GradeStatistics>, theme: String)
|
fun updatePieData(items: List<GradeStatistics>, theme: String)
|
||||||
|
|
||||||
|
fun updateBarData(item: GradePointsStatistics)
|
||||||
|
|
||||||
fun showSubjects(show: Boolean)
|
fun showSubjects(show: Boolean)
|
||||||
|
|
||||||
@ -21,7 +26,9 @@ interface GradeStatisticsView : BaseView {
|
|||||||
|
|
||||||
fun clearView()
|
fun clearView()
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showPieContent(show: Boolean)
|
||||||
|
|
||||||
|
fun showBarContent(show: Boolean)
|
||||||
|
|
||||||
fun showEmpty(show: Boolean)
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.grade.statistics
|
||||||
|
|
||||||
|
enum class ViewType {
|
||||||
|
SEMESTER,
|
||||||
|
PARTIAL,
|
||||||
|
POINTS
|
||||||
|
}
|
@ -72,6 +72,13 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:tag="annual"
|
android:tag="annual"
|
||||||
android:text="@string/grade_statistics_semester" />
|
android:text="@string/grade_statistics_semester" />
|
||||||
|
|
||||||
|
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
|
android:id="@+id/gradeStatisticsTypePoints"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:tag="points"
|
||||||
|
android:text="@string/grade_statistics_points" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
@ -88,6 +95,16 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.github.mikephil.charting.charts.BarChart
|
||||||
|
android:id="@+id/gradeStatisticsChartPoints"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:background="?android:windowBackground"
|
||||||
|
android:minHeight="400dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
|
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
|
||||||
android:id="@+id/gradeStatisticsProgress"
|
android:id="@+id/gradeStatisticsProgress"
|
||||||
style="@style/Widget.MaterialProgressBar.ProgressBar"
|
style="@style/Widget.MaterialProgressBar.ProgressBar"
|
||||||
|
@ -66,8 +66,9 @@
|
|||||||
<string name="grade_menu_summary">Podsumowanie</string>
|
<string name="grade_menu_summary">Podsumowanie</string>
|
||||||
<string name="grade_menu_statistics">Klasa</string>
|
<string name="grade_menu_statistics">Klasa</string>
|
||||||
<string name="grade_menu_read">Oznacz jako przeczytane</string>
|
<string name="grade_menu_read">Oznacz jako przeczytane</string>
|
||||||
<string name="grade_statistics_partial">Oceny cząstkowe</string>
|
<string name="grade_statistics_partial">Cząstkowe</string>
|
||||||
<string name="grade_statistics_semester">Oceny semestralne</string>
|
<string name="grade_statistics_semester">Semestralne</string>
|
||||||
|
<string name="grade_statistics_points">Punkty</string>
|
||||||
<plurals name="grade_number_item">
|
<plurals name="grade_number_item">
|
||||||
<item quantity="one">%d ocena</item>
|
<item quantity="one">%d ocena</item>
|
||||||
<item quantity="few">%d oceny</item>
|
<item quantity="few">%d oceny</item>
|
||||||
|
@ -66,8 +66,9 @@
|
|||||||
<string name="grade_menu_summary">Summary</string>
|
<string name="grade_menu_summary">Summary</string>
|
||||||
<string name="grade_menu_statistics">Class</string>
|
<string name="grade_menu_statistics">Class</string>
|
||||||
<string name="grade_menu_read">Mark as read</string>
|
<string name="grade_menu_read">Mark as read</string>
|
||||||
<string name="grade_statistics_partial">Partial grades</string>
|
<string name="grade_statistics_partial">Partial</string>
|
||||||
<string name="grade_statistics_semester">Semester grades</string>
|
<string name="grade_statistics_semester">Semester</string>
|
||||||
|
<string name="grade_statistics_points">Points</string>
|
||||||
<plurals name="grade_number_item">
|
<plurals name="grade_number_item">
|
||||||
<item quantity="one">%d grade</item>
|
<item quantity="one">%d grade</item>
|
||||||
<item quantity="other">%d grades</item>
|
<item quantity="other">%d grades</item>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.repositories.gradestatistics
|
package io.github.wulkanowy.data.repositories.gradestatistics
|
||||||
|
|
||||||
import io.github.wulkanowy.api.Api
|
import io.github.wulkanowy.api.Api
|
||||||
|
import io.github.wulkanowy.api.grades.GradePointsSummary
|
||||||
import io.github.wulkanowy.api.grades.GradeStatistics
|
import io.github.wulkanowy.api.grades.GradeStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.mockk.MockKAnnotations
|
import io.mockk.MockKAnnotations
|
||||||
@ -8,7 +9,7 @@ import io.mockk.every
|
|||||||
import io.mockk.impl.annotations.MockK
|
import io.mockk.impl.annotations.MockK
|
||||||
import io.mockk.impl.annotations.SpyK
|
import io.mockk.impl.annotations.SpyK
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.junit.Assert
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ class GradeStatisticsRemoteTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getGradeStatisticsTest() {
|
fun getGradeStatisticsTest() {
|
||||||
every { mockApi.getGradesStatistics(1, any()) } returns Single.just(listOf(
|
every { mockApi.getGradesPartialStatistics(1) } returns Single.just(listOf(
|
||||||
getGradeStatistics("Fizyka"),
|
getGradeStatistics("Fizyka"),
|
||||||
getGradeStatistics("Matematyka")
|
getGradeStatistics("Matematyka")
|
||||||
))
|
))
|
||||||
@ -39,7 +40,24 @@ class GradeStatisticsRemoteTest {
|
|||||||
every { semesterMock.diaryId } returns 1
|
every { semesterMock.diaryId } returns 1
|
||||||
|
|
||||||
val stats = GradeStatisticsRemote(mockApi).getGradeStatistics(semesterMock, false).blockingGet()
|
val stats = GradeStatisticsRemote(mockApi).getGradeStatistics(semesterMock, false).blockingGet()
|
||||||
Assert.assertEquals(2, stats.size)
|
assertEquals(2, stats.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getGradePointsStatisticsTest() {
|
||||||
|
every { mockApi.getGradesPointsStatistics(1) } returns Single.just(listOf(
|
||||||
|
getGradePointsStatistics("Fizyka"),
|
||||||
|
getGradePointsStatistics("Matematyka")
|
||||||
|
))
|
||||||
|
|
||||||
|
every { mockApi.diaryId } returns 1
|
||||||
|
every { semesterMock.studentId } returns 1
|
||||||
|
every { semesterMock.semesterId } returns 1
|
||||||
|
every { semesterMock.semesterName } returns 2
|
||||||
|
every { semesterMock.diaryId } returns 1
|
||||||
|
|
||||||
|
val stats = GradeStatisticsRemote(mockApi).getGradePointsStatistics(semesterMock).blockingGet()
|
||||||
|
assertEquals(2, stats.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGradeStatistics(subjectName: String): GradeStatistics {
|
private fun getGradeStatistics(subjectName: String): GradeStatistics {
|
||||||
@ -49,4 +67,12 @@ class GradeStatisticsRemoteTest {
|
|||||||
amount = 10
|
amount = 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getGradePointsStatistics(subjectName: String): GradePointsSummary {
|
||||||
|
return GradePointsSummary(
|
||||||
|
subject = subjectName,
|
||||||
|
student = 0.80,
|
||||||
|
others = 0.40
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user