Add grade statistics (#251)

This commit is contained in:
Mikołaj Pich
2019-03-04 12:13:37 +01:00
committed by Rafał Borcz
parent cae4f140e6
commit dcab8df4b9
30 changed files with 881 additions and 25 deletions

View File

@ -82,6 +82,10 @@ internal class RepositoryModule {
@Provides
fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao
@Singleton
@Provides
fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics
@Singleton
@Provides
fun provideMessagesDao(database: AppDatabase) = database.messagesDao

View File

@ -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.ExamDao
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
@ -27,6 +28,7 @@ import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.LuckyNumber
@ -43,6 +45,7 @@ import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6
import io.github.wulkanowy.data.db.migrations.Migration7
import javax.inject.Singleton
@Singleton
@ -56,6 +59,7 @@ import javax.inject.Singleton
AttendanceSummary::class,
Grade::class,
GradeSummary::class,
GradeStatistics::class,
Message::class,
Note::class,
Homework::class,
@ -72,7 +76,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 6
const val VERSION_SCHEMA = 7
fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
@ -84,7 +88,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration3(),
Migration4(),
Migration5(),
Migration6()
Migration6(),
Migration7()
)
.build()
}
@ -106,6 +111,8 @@ abstract class AppDatabase : RoomDatabase() {
abstract val gradeSummaryDao: GradeSummaryDao
abstract val gradeStatistics: GradeStatisticsDao
abstract val messagesDao: MessagesDao
abstract val noteDao: NoteDao

View File

@ -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.GradeStatistics
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface GradeStatisticsDao {
@Insert
fun insertAll(gradesStatistics: List<GradeStatistics>)
@Delete
fun deleteAll(gradesStatistics: List<GradeStatistics>)
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe<List<GradeStatistics>>
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Maybe<List<GradeStatistics>>
}

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "GradesStatistics")
data class GradeStatistics(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "semester_id")
val semesterId: Int,
val subject: String,
val grade: Int,
val amount: Int,
@ColumnInfo(name = "is_semester")
val semester: Boolean
) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,18 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration7 : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `GradesStatistics` (" +
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
"`student_id` INTEGER NOT NULL," +
"`semester_id` INTEGER NOT NULL," +
"`subject` TEXT NOT NULL," +
"`grade` INTEGER NOT NULL," +
"`amount` INTEGER NOT NULL," +
"`is_semester` INTEGER NOT NULL)")
}
}

View File

@ -0,0 +1,34 @@
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) {
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe<List<GradeStatistics>> {
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
.filter { !it.isEmpty() }
}
fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe<List<GradeStatistics>> {
return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list ->
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.isEmpty() }
}
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.insertAll(gradesStatistics)
}
fun deleteGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.deleteAll(gradesStatistics)
}
}

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeStatisticsRemote @Inject constructor(private val api: Api) {
fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getGradesStatistics(semester.semesterId, isSemester) }
.map { gradeStatistics ->
gradeStatistics.map {
GradeStatistics(
semesterId = semester.semesterId,
studentId = semester.studentId,
subject = it.subject,
grade = it.gradeValue,
amount = it.amount ?: 0,
semester = isSemester
)
}
}
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.data.repositories.gradestatistics
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeStatisticsRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: GradeStatisticsLocal,
private val remote: GradeStatisticsRemote
) {
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatistics>> {
return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getGradeStatistics(semester, isSemester)
else Single.error(UnknownHostException())
}.flatMap { newGradesStats ->
local.getGradesStatistics(semester, isSemester).toSingle(emptyList())
.doOnSuccess { oldGradesStats ->
local.deleteGradesStatistics(oldGradesStats - newGradesStats)
local.saveGradesStatistics(newGradesStats - oldGradesStats)
}
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
}
}

View File

@ -10,7 +10,7 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SubjectRepostory @Inject constructor(
class SubjectRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: SubjectLocal,
private val remote: SubjectRemote

View File

@ -5,7 +5,7 @@ import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepostory
import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
@ -21,7 +21,7 @@ import javax.inject.Inject
class AttendanceSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val subjectRepository: SubjectRepostory,
private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val schedulers: SchedulersProvider,

View File

@ -0,0 +1,34 @@
package io.github.wulkanowy.ui.modules.grade
import android.content.Context
import android.util.AttributeSet
import android.view.ViewGroup
import com.google.android.material.tabs.TabLayout
/**
* @see <a href="https://stackoverflow.com/a/50382854">Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout</a>
*/
class CustomTabLayout : TabLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec)
val tabLayout = getChildAt(0) as ViewGroup
val childCount = tabLayout.childCount
if (childCount == 0) return
val tabMinWidth = context.resources.displayMetrics.widthPixels / childCount
for (i in 0 until childCount) {
tabLayout.getChildAt(i).minimumWidth = tabMinWidth
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}

View File

@ -14,6 +14,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.setOnSelectPageListener
@ -63,12 +64,14 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
containerId = gradeViewPager.id
addFragmentsWithTitle(mapOf(
GradeDetailsFragment.newInstance() to getString(R.string.all_details),
GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary)
GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary),
GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics)
))
}
gradeViewPager.run {
adapter = pagerAdapter
offscreenPageLimit = 3
setOnSelectPageListener { presenter.onPageSelected(it) }
}
gradeTabLayout.setupWithViewPager(gradeViewPager)

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.di.scopes.PerChildFragment
import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
@Module
@ -28,4 +29,8 @@ abstract class GradeModule {
@PerChildFragment
@ContributesAndroidInjector
abstract fun binGradeSummaryFragment(): GradeSummaryFragment
@PerChildFragment
@ContributesAndroidInjector
abstract fun binGradeStatisticsFragment(): GradeStatisticsFragment
}

View File

@ -114,6 +114,6 @@ class GradePresenter @Inject constructor(
}
private fun notifyChildrenSemesterChange() {
for (i in 0..1) view?.notifyChildSemesterChange(i)
for (i in 0..2) view?.notifyChildSemesterChange(i)
}
}

View File

@ -0,0 +1,191 @@
package io.github.wulkanowy.ui.modules.grade.statistics
import android.graphics.Color.WHITE
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.components.LegendEntry
import com.github.mikephil.charting.data.PieData
import com.github.mikephil.charting.data.PieDataSet
import com.github.mikephil.charting.data.PieEntry
import com.github.mikephil.charting.formatter.ValueFormatter
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.setOnItemSelectedListener
import kotlinx.android.synthetic.main.fragment_grade_statistics.*
import javax.inject.Inject
class GradeStatisticsFragment : BaseSessionFragment(), GradeStatisticsView, GradeView.GradeChildView {
@Inject
lateinit var presenter: GradeStatisticsPresenter
private lateinit var subjectsAdapter: ArrayAdapter<String>
companion object {
private const val SAVED_CHART_TYPE = "CURRENT_TYPE"
fun newInstance() = GradeStatisticsFragment()
}
override val isViewEmpty
get() = gradeStatisticsChart.isEmpty
private val gradeColors = listOf(
6 to R.color.grade_material_six,
5 to R.color.grade_material_five,
4 to R.color.grade_material_four,
3 to R.color.grade_material_three,
2 to R.color.grade_material_two,
1 to R.color.grade_material_one
)
private val gradeLabels = listOf(
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_grade_statistics, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
messageContainer = gradeStatisticsChart
presenter.onAttachView(this, savedInstanceState?.getBoolean(SAVED_CHART_TYPE))
}
override fun initView() {
gradeStatisticsChart.run {
description.isEnabled = false
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
animateXY(1000, 1000)
minAngleForSlices = 25f
legend.apply {
textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
setCustom(gradeLabels.mapIndexed { i, it ->
LegendEntry().apply {
label = it
formColor = ContextCompat.getColor(context, gradeColors[i].second)
form = Legend.LegendForm.SQUARE
}
})
}
}
context?.let {
subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList<String>())
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
}
gradeStatisticsSubjects.run {
adapter = subjectsAdapter
setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) }
}
gradeStatisticsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
}
override fun updateSubjects(data: ArrayList<String>) {
subjectsAdapter.run {
clear()
addAll(data)
notifyDataSetChanged()
}
}
override fun updateData(items: List<GradeStatistics>) {
gradeStatisticsChart.run {
data = PieData(PieDataSet(items.map {
PieEntry(it.amount.toFloat(), it.grade.toString())
}, "Legenda").apply {
valueTextSize = 12f
sliceSpace = 1f
valueTextColor = WHITE
setColors(items.map {
gradeColors.single { color -> color.first == it.grade }.second
}.toIntArray(), context)
}).apply {
setTouchEnabled(false)
setValueFormatter(object : ValueFormatter() {
override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt())
}
})
centerText = items.fold(0) { acc, it -> acc + it.amount }
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
}
invalidate()
}
}
override fun showSubjects(show: Boolean) {
gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.INVISIBLE
}
override fun clearView() {
gradeStatisticsChart.clear()
}
override fun showContent(show: Boolean) {
gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showEmpty(show: Boolean) {
gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE
}
override fun showProgress(show: Boolean) {
gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showRefresh(show: Boolean) {
gradeStatisticsSwipe.isRefreshing = show
}
override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) {
presenter.onParentViewLoadData(semesterId, forceRefresh)
}
override fun onParentReselected() {
//
}
override fun onParentChangeSemester() {
presenter.onParentViewChangeSemester()
}
override fun notifyParentDataLoaded(semesterId: Int) {
(parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId)
}
override fun notifyParentRefresh() {
(parentFragment as? GradeFragment)?.onChildRefresh()
}
override fun onResume() {
super.onResume()
gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
presenter.onTypeChange(checkedId == R.id.gradeStatisticsTypeSemester)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(GradeStatisticsFragment.SAVED_CHART_TYPE, presenter.currentIsSemester)
}
override fun onDestroyView() {
super.onDestroyView()
presenter.onDetachView()
}
}

View File

@ -0,0 +1,140 @@
package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class GradeStatisticsPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val gradeStatisticsRepository: GradeStatisticsRepository,
private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeStatisticsView>(errorHandler) {
private var subjects = emptyList<Subject>()
private var currentSemesterId = 0
private var currentSubjectName: String = "Wszystkie"
var currentIsSemester = false
private set
fun onAttachView(view: GradeStatisticsView, isSemester: Boolean?) {
super.onAttachView(view)
currentIsSemester = isSemester ?: false
view.initView()
}
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
currentSemesterId = semesterId
loadSubjects()
loadData(semesterId, currentSubjectName, currentIsSemester, forceRefresh)
}
fun onParentViewChangeSemester() {
view?.run {
showProgress(true)
showRefresh(false)
showContent(false)
showEmpty(false)
clearView()
}
disposable.clear()
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the grade stats")
view?.notifyParentRefresh()
}
fun onSubjectSelected(name: String) {
Timber.i("Select attendance stats subject $name")
view?.run {
showContent(false)
showProgress(true)
showEmpty(false)
clearView()
}
(subjects.singleOrNull { it.name == name }?.name).let {
if (it != currentSubjectName) loadData(currentSemesterId, name, currentIsSemester)
}
}
fun onTypeChange(isSemester: Boolean) {
Timber.i("Select attendance stats semester: $isSemester")
view?.run {
showContent(false)
showProgress(true)
showEmpty(false)
clearView()
}
loadData(currentSemesterId, currentSubjectName, isSemester)
}
private fun loadSubjects() {
Timber.i("Loading grade stats subjects started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { subjectRepository.getSubjects(it) }
.doOnSuccess { subjects = it }
.map { ArrayList(it.map { subject -> subject.name }) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading grade stats subjects result: Success")
view?.run {
updateSubjects(it)
showSubjects(true)
}
}, {
Timber.e("Loading grade stats subjects result: An exception occurred")
errorHandler.dispatch(it)
})
)
}
private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) {
Timber.i("Loading grade stats data started")
currentSubjectName = subjectName
currentIsSemester = isSemester
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it) }
.flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) }
.map { list -> list.sortedByDescending { it.grade } }
.map { list -> list.filter { it.amount != 0 } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
notifyParentDataLoaded(semesterId)
}
}
.subscribe({
Timber.i("Loading grade stats result: Success")
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateData(it)
}
analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.e("Loading grade stats result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
}

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.ui.base.session.BaseSessionView
interface GradeStatisticsView : BaseSessionView {
val isViewEmpty: Boolean
fun initView()
fun updateSubjects(data: ArrayList<String>)
fun updateData(items: List<GradeStatistics>)
fun showSubjects(show: Boolean)
fun notifyParentDataLoaded(semesterId: Int)
fun notifyParentRefresh()
fun clearView()
fun showContent(show: Boolean)
fun showEmpty(show: Boolean)
fun showProgress(show: Boolean)
fun showRefresh(show: Boolean)
}

View File

@ -4,11 +4,19 @@ import android.view.View
import android.widget.AdapterView
import android.widget.Spinner
/**
* @see <a href="https://stackoverflow.com/a/29602298">How to keep onItemSelected from firing off on a newly instantiated Spinner?</a>
*/
inline fun Spinner.setOnItemSelectedListener(crossinline listener: (view: View?) -> Unit) {
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
listener(view)
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
listener(view)
}
}
}
}
}