1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-19 20:19:09 -05:00

Save semesters with students during registration (#915)

This commit is contained in:
Mikołaj Pich 2020-08-26 10:25:01 +02:00 committed by GitHub
parent ca7d977342
commit d5187d1808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 239 additions and 162 deletions

View File

@ -4,7 +4,16 @@
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />

View File

@ -126,13 +126,12 @@ configurations.all {
}
dependencies {
implementation "io.github.wulkanowy:sdk:02486b9"
implementation "io.github.wulkanowy:sdk:b919b96"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation "androidx.core:core-ktx:1.3.1"
implementation "androidx.activity:activity-ktx:1.1.0"
@ -195,7 +194,7 @@ dependencies {
testImplementation "junit:junit:4.13"
testImplementation "io.mockk:mockk:$mockk"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.8'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9'
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester
import javax.inject.Singleton
@ -9,6 +11,9 @@ import javax.inject.Singleton
@Dao
interface SemesterDao : BaseDao<Semester> {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertSemesters(items: List<Semester>): List<Long>
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
}

View File

@ -5,7 +5,9 @@ import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy.ABORT
import androidx.room.Query
import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import javax.inject.Singleton
@Singleton
@ -27,6 +29,10 @@ interface StudentDao {
@Query("SELECT * FROM Students")
suspend fun loadAll(): List<Student>
@Transaction
@Query("SELECT * FROM Students")
suspend fun loadStudentsWithSemesters(): List<StudentWithSemesters>
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
suspend fun updateCurrent(id: Long)

View File

@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
@ -36,7 +37,7 @@ data class Semester(
@ColumnInfo(name = "unit_id")
val unitId: Int
) {
): Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.Embedded
import androidx.room.Relation
import java.io.Serializable
data class StudentWithSemesters(
@Embedded
val student: Student,
@Relation(parentColumn = "student_id", entityColumn = "student_id")
val semesters: List<Semester>
) : Serializable

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester
fun List<SdkSemester>.mapToEntities(studentId: Int) = map {
Semester(
studentId = studentId,
diaryId = it.diaryId,
diaryName = it.diaryName,
schoolYear = it.schoolYear,
semesterId = it.semesterId,
semesterName = it.semesterNumber,
start = it.start,
end = it.end,
classId = it.classId,
unitId = it.unitId
)
}

View File

@ -0,0 +1,34 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import java.time.LocalDateTime
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
fun List<SdkStudent>.mapToEntities(password: String = "") = map {
StudentWithSemesters(
student = Student(
email = it.email,
password = password,
isParent = it.isParent,
symbol = it.symbol,
studentId = it.studentId,
userLoginId = it.userLoginId,
studentName = it.studentName + " " + it.studentSurname,
schoolSymbol = it.schoolSymbol,
schoolShortName = it.schoolShortName,
schoolName = it.schoolName,
className = it.className,
classId = it.classId,
scrapperBaseUrl = it.scrapperBaseUrl,
loginType = it.loginType.name,
isCurrent = false,
registrationDate = LocalDateTime.now(),
mobileBaseUrl = it.mobileBaseUrl,
privateKey = it.privateKey,
certificateKey = it.certificateKey,
loginMode = it.loginMode.name
),
semesters = it.semesters.mapToEntities(it.studentId)
)
}

View File

@ -30,7 +30,7 @@ class GradeRepository @Inject constructor(
)
private suspend fun refreshGradeDetails(student: Student, oldGrades: List<Grade>, newDetails: List<Grade>, notify: Boolean) {
val notifyBreakDate = oldGrades.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate()
local.deleteGrades(oldGrades uniqueSubtract newDetails)
local.saveGrades((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {

View File

@ -10,7 +10,7 @@ import javax.inject.Singleton
class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
suspend fun saveSemesters(semesters: List<Semester>) {
semesterDb.insertAll(semesters)
semesterDb.insertSemesters(semesters)
}
suspend fun deleteSemesters(semesters: List<Semester>) {

View File

@ -27,9 +27,9 @@ class StudentLocal @Inject constructor(
}
suspend fun getStudents(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
studentDb.loadAll().map {
studentDb.loadStudentsWithSemesters().map {
it.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) student.password = decrypt(student.password)
}
}
}

View File

@ -1,51 +1,23 @@
package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.sdk.Sdk
import java.time.LocalDateTime.now
import javax.inject.Inject
import javax.inject.Singleton
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
@Singleton
class StudentRemote @Inject constructor(private val sdk: Sdk) {
private fun mapStudents(students: List<SdkStudent>, email: String, password: String): List<Student> {
return students.map { student ->
Student(
email = email.ifBlank { student.email },
password = password,
isParent = student.isParent,
symbol = student.symbol,
studentId = student.studentId,
userLoginId = student.userLoginId,
studentName = student.studentName,
schoolSymbol = student.schoolSymbol,
schoolShortName = student.schoolShortName,
schoolName = student.schoolName,
className = student.className,
classId = student.classId,
scrapperBaseUrl = student.scrapperBaseUrl,
loginType = student.loginType.name,
isCurrent = false,
registrationDate = now(),
mobileBaseUrl = student.mobileBaseUrl,
privateKey = student.privateKey,
certificateKey = student.certificateKey,
loginMode = student.loginMode.name
)
}
suspend fun getStudentsMobileApi(token: String, pin: String, symbol: String): List<StudentWithSemesters> {
return sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities()
}
suspend fun getStudentsMobileApi(token: String, pin: String, symbol: String): List<Student> {
return mapStudents(sdk.getStudentsFromMobileApi(token, pin, symbol, ""), "", "")
suspend fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<StudentWithSemesters> {
return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).mapToEntities(password)
}
suspend fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<Student> {
return mapStudents(sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol), email, password)
}
suspend fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<Student> {
return mapStudents(sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol), email, password)
suspend fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<StudentWithSemesters> {
return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password)
}
}

View File

@ -2,12 +2,15 @@ package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.semester.SemesterLocal
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class StudentRepository @Inject constructor(
private val local: StudentLocal,
private val semestersLocal: SemesterLocal,
private val remote: StudentRemote
) {
@ -15,19 +18,19 @@ class StudentRepository @Inject constructor(
suspend fun isCurrentStudentSet(): Boolean = local.getCurrentStudent(false)?.isCurrent ?: false
suspend fun getStudentsApi(pin: String, symbol: String, token: String): List<Student> {
suspend fun getStudentsApi(pin: String, symbol: String, token: String): List<StudentWithSemesters> {
return remote.getStudentsMobileApi(token, pin, symbol)
}
suspend fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String): List<Student> {
suspend fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String): List<StudentWithSemesters> {
return remote.getStudentsScrapper(email, password, endpoint, symbol)
}
suspend fun getStudentsHybrid(email: String, password: String, endpoint: String, symbol: String): List<Student> {
suspend fun getStudentsHybrid(email: String, password: String, endpoint: String, symbol: String): List<StudentWithSemesters> {
return remote.getStudentsHybrid(email, password, endpoint, symbol)
}
suspend fun getSavedStudents(decryptPass: Boolean = true): List<Student> {
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
return local.getStudents(decryptPass)
}
@ -39,12 +42,13 @@ class StudentRepository @Inject constructor(
return local.getCurrentStudent(decryptPass) ?: throw NoCurrentStudentException()
}
suspend fun saveStudents(students: List<Student>): List<Long> {
return local.saveStudents(students)
suspend fun saveStudents(studentsWithSemesters: List<StudentWithSemesters>): List<Long> {
semestersLocal.saveSemesters(studentsWithSemesters.flatMap { it.semesters })
return local.saveStudents(studentsWithSemesters.map { it.student })
}
suspend fun switchStudent(student: Student) {
return local.setCurrentStudent(student)
suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) {
return local.setCurrentStudent(studentWithSemesters.student)
}
suspend fun logoutStudent(student: Student) {

View File

@ -9,6 +9,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.HeaderAccountBinding
import io.github.wulkanowy.databinding.ItemAccountBinding
import io.github.wulkanowy.sdk.Sdk
@ -19,7 +20,7 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
var items = emptyList<AccountItem<*>>()
var onClickListener: (Student) -> Unit = {}
var onClickListener: (StudentWithSemesters) -> Unit = {}
override fun getItemCount() = items.size
@ -38,7 +39,7 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as Account)
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as Student)
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as StudentWithSemesters)
}
}
@ -50,10 +51,14 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
}
@SuppressLint("SetTextI18n")
private fun bindItemViewHolder(binding: ItemAccountBinding, student: Student) {
private fun bindItemViewHolder(binding: ItemAccountBinding, studentWithSemesters: StudentWithSemesters) {
val student = studentWithSemesters.student
val semesters = studentWithSemesters.semesters
val diary = semesters.maxByOrNull { it.semesterId }
with(binding) {
accountItemName.text = "${student.studentName} ${student.className}"
accountItemSchool.text = student.schoolName
accountItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
accountItemSchool.text = studentWithSemesters.student.schoolName
with(accountItemLoginMode) {
visibility = when (Sdk.Mode.valueOf(student.loginMode)) {
Sdk.Mode.API -> {
@ -77,7 +82,7 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
}
root.setOnClickListener { onClickListener(student) }
root.setOnClickListener { onClickListener(studentWithSemesters) }
}
}

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
@ -68,11 +68,11 @@ class AccountPresenter @Inject constructor(
}.launch("logout")
}
fun onItemSelected(student: Student) {
Timber.i("Select student item ${student.id}")
if (student.isCurrent) {
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
Timber.i("Select student item ${studentWithSemesters.student.id}")
if (studentWithSemesters.student.isCurrent) {
view?.dismissView()
} else flowWithResource { studentRepository.switchStudent(student) }.onEach {
} else flowWithResource { studentRepository.switchStudent(studentWithSemesters) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to change a student")
Status.SUCCESS -> {
@ -89,8 +89,8 @@ class AccountPresenter @Inject constructor(
}.launch("switch")
}
private fun createAccountItems(items: List<Student>): List<AccountItem<*>> {
return items.groupBy { Account(it.email, it.isParent) }.map { (account, students) ->
private fun createAccountItems(items: List<StudentWithSemesters>): List<AccountItem<*>> {
return items.groupBy { Account(it.student.email, it.student.isParent) }.map { (account, students) ->
listOf(AccountItem(account, AccountItem.ViewType.HEADER)) + students.map { student ->
AccountItem(student, AccountItem.ViewType.ITEM)
}

View File

@ -5,7 +5,7 @@ import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ActivityLoginBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
@ -86,16 +86,16 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
(loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment(loginData)
}
override fun notifyInitStudentSelectFragment(students: List<Student>) {
(loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)?.onParentInitStudentSelectFragment(students)
override fun notifyInitStudentSelectFragment(studentsWithSemesters: List<StudentWithSemesters>) {
(loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)?.onParentInitStudentSelectFragment(studentsWithSemesters)
}
fun onFormFragmentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
presenter.onFormViewAccountLogged(students, loginData)
fun onFormFragmentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>, loginData: Triple<String, String, String>) {
presenter.onFormViewAccountLogged(studentsWithSemesters, loginData)
}
fun onSymbolFragmentAccountLogged(students: List<Student>) {
presenter.onSymbolViewAccountLogged(students)
fun onSymbolFragmentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>) {
presenter.onSymbolViewAccountLogged(studentsWithSemesters)
}
fun onAdvancedLoginClick() {

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.login
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@ -21,24 +21,24 @@ class LoginPresenter @Inject constructor(
Timber.i("Login view was initialized")
}
fun onFormViewAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
fun onFormViewAccountLogged(studentsWithSemesters: List<StudentWithSemesters>, loginData: Triple<String, String, String>) {
view?.apply {
if (students.isEmpty()) {
if (studentsWithSemesters.isEmpty()) {
Timber.i("Switch to symbol form")
notifyInitSymbolFragment(loginData)
switchView(1)
} else {
Timber.i("Switch to student select")
notifyInitStudentSelectFragment(students)
notifyInitStudentSelectFragment(studentsWithSemesters)
switchView(2)
}
}
}
fun onSymbolViewAccountLogged(students: List<Student>) {
fun onSymbolViewAccountLogged(studentsWithSemesters: List<StudentWithSemesters>) {
view?.apply {
Timber.i("Switch to student select")
notifyInitStudentSelectFragment(students)
notifyInitStudentSelectFragment(studentsWithSemesters)
switchView(2)
}
}

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.login
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface LoginView : BaseView {
@ -15,5 +15,5 @@ interface LoginView : BaseView {
fun notifyInitSymbolFragment(loginData: Triple<String, String, String>)
fun notifyInitStudentSelectFragment(students: List<Student>)
fun notifyInitStudentSelectFragment(studentsWithSemesters: List<StudentWithSemesters>)
}

View File

@ -8,7 +8,7 @@ import android.widget.ArrayAdapter
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BaseFragment
@ -295,8 +295,8 @@ class LoginAdvancedFragment :
binding.loginFormContainer.visibility = if (show) VISIBLE else GONE
}
override fun notifyParentAccountLogged(students: List<Student>) {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
override fun notifyParentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>) {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(studentsWithSemesters, Triple(
binding.loginFormUsername.text.toString(),
binding.loginFormPass.text.toString(),
resources.getStringArray(R.array.hosts_values)[1]

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BasePresenter
@ -161,7 +161,7 @@ class LoginAdvancedPresenter @Inject constructor(
}.launch("login")
}
private suspend fun getStudentsAppropriatesToLoginType(): List<Student> {
private suspend fun getStudentsAppropriatesToLoginType(): List<StudentWithSemesters> {
val email = view?.formUsernameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty()

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface LoginAdvancedView : BaseView {
@ -69,7 +69,7 @@ interface LoginAdvancedView : BaseView {
fun showContent(show: Boolean)
fun notifyParentAccountLogged(students: List<Student>)
fun notifyParentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>)
fun setErrorPinRequired()

View File

@ -8,7 +8,7 @@ import android.view.View.VISIBLE
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
@ -171,8 +171,8 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
binding.loginFormVersion.text = "v${appInfo.versionName}"
}
override fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, loginData)
override fun notifyParentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>, loginData: Triple<String, String, String>) {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(studentsWithSemesters, loginData)
}
override fun openPrivacyPolicyPage() {

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface LoginFormView : BaseView {
@ -49,7 +49,7 @@ interface LoginFormView : BaseView {
fun showVersion()
fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>)
fun notifyParentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>, loginData: Triple<String, String, String>)
fun openPrivacyPolicyPage()

View File

@ -5,7 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding
import javax.inject.Inject
@ -14,13 +14,13 @@ class LoginStudentSelectAdapter @Inject constructor() :
private val checkedList = mutableMapOf<Int, Boolean>()
var items = emptyList<Pair<Student, Boolean>>()
var items = emptyList<Pair<StudentWithSemesters, Boolean>>()
set(value) {
field = value
checkedList.clear()
}
var onClickListener: (Student, alreadySaved: Boolean) -> Unit = { _, _ -> }
var onClickListener: (StudentWithSemesters, alreadySaved: Boolean) -> Unit = { _, _ -> }
override fun getItemCount() = items.size
@ -30,10 +30,13 @@ class LoginStudentSelectAdapter @Inject constructor() :
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val (student, alreadySaved) = items[position]
val (studentAndSemesters, alreadySaved) = items[position]
val student = studentAndSemesters.student
val semesters = studentAndSemesters.semesters
val diary = semesters.maxByOrNull { it.semesterId }
with(holder.binding) {
loginItemName.text = "${student.studentName} ${student.className}"
loginItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
loginItemSchool.text = student.schoolName
loginItemName.isEnabled = !alreadySaved
loginItemSchool.isEnabled = !alreadySaved
@ -46,7 +49,7 @@ class LoginStudentSelectAdapter @Inject constructor() :
}
root.setOnClickListener {
onClickListener(student, alreadySaved)
onClickListener(studentAndSemesters, alreadySaved)
with(loginItemCheck) {
if (isEnabled) {

View File

@ -7,7 +7,7 @@ import android.view.View.VISIBLE
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
@ -58,7 +58,7 @@ class LoginStudentSelectFragment :
}
}
override fun updateData(data: List<Pair<Student, Boolean>>) {
override fun updateData(data: List<Pair<StudentWithSemesters, Boolean>>) {
with(loginAdapter) {
items = data
notifyDataSetChanged()
@ -81,8 +81,8 @@ class LoginStudentSelectFragment :
binding.loginStudentSelectSignIn.isEnabled = enable
}
fun onParentInitStudentSelectFragment(students: List<Student>) {
presenter.onParentInitStudentSelectView(students)
fun onParentInitStudentSelectFragment(studentsWithSemesters: List<StudentWithSemesters>) {
presenter.onParentInitStudentSelectView(studentsWithSemesters)
}
override fun onSaveInstanceState(outState: Bundle) {

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.studentselect
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
@ -21,9 +22,9 @@ class LoginStudentSelectPresenter @Inject constructor(
private var lastError: Throwable? = null
var students = emptyList<Student>()
var students = emptyList<StudentWithSemesters>()
private val selectedStudents = mutableListOf<Student>()
private val selectedStudents = mutableListOf<StudentWithSemesters>()
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
super.onAttachView(view)
@ -38,7 +39,7 @@ class LoginStudentSelectPresenter @Inject constructor(
}
if (students is List<*> && students.isNotEmpty()) {
loadData(students.filterIsInstance<Student>())
loadData(students.filterIsInstance<StudentWithSemesters>())
}
}
@ -46,17 +47,17 @@ class LoginStudentSelectPresenter @Inject constructor(
registerStudents(selectedStudents)
}
fun onParentInitStudentSelectView(students: List<Student>) {
loadData(students)
if (students.size == 1) registerStudents(students)
fun onParentInitStudentSelectView(studentsWithSemesters: List<StudentWithSemesters>) {
loadData(studentsWithSemesters)
if (studentsWithSemesters.size == 1) registerStudents(studentsWithSemesters)
}
fun onItemSelected(student: Student, alreadySaved: Boolean) {
fun onItemSelected(studentWithSemester: StudentWithSemesters, alreadySaved: Boolean) {
if (alreadySaved) return
selectedStudents
.removeAll { it == student }
.let { if (!it) selectedStudents.add(student) }
.removeAll { it == studentWithSemester }
.let { if (!it) selectedStudents.add(studentWithSemester) }
view?.enableSignIn(selectedStudents.isNotEmpty())
}
@ -69,20 +70,20 @@ class LoginStudentSelectPresenter @Inject constructor(
&& a.classId == b.classId
}
private fun loadData(students: List<Student>) {
private fun loadData(studentsWithSemesters: List<StudentWithSemesters>) {
resetSelectedState()
this.students = students
this.students = studentsWithSemesters
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Login student select students load started")
Status.SUCCESS -> view?.updateData(students.map { student ->
student to it.data!!.any { item -> compareStudents(student, item) }
Status.SUCCESS -> view?.updateData(studentsWithSemesters.map { studentWithSemesters ->
studentWithSemesters to it.data!!.any { item -> compareStudents(studentWithSemesters.student, item.student) }
})
Status.ERROR -> {
errorHandler.dispatch(it.error!!)
lastError = it.error
view?.updateData(students.map { student -> student to false })
view?.updateData(studentsWithSemesters.map { student -> student to false })
}
}
}.launch()
@ -93,10 +94,10 @@ class LoginStudentSelectPresenter @Inject constructor(
view?.enableSignIn(false)
}
private fun registerStudents(students: List<Student>) {
private fun registerStudents(studentsWithSemesters: List<StudentWithSemesters>) {
flowWithResource {
val savedStudents = studentRepository.saveStudents(students)
val firstRegistered = students.first().apply { id = savedStudents.first() }
val savedStudents = studentRepository.saveStudents(studentsWithSemesters)
val firstRegistered = studentsWithSemesters.first().apply { student.id = savedStudents.first() }
studentRepository.switchStudent(firstRegistered)
}.onEach {
when (it.status) {
@ -108,7 +109,7 @@ class LoginStudentSelectPresenter @Inject constructor(
Status.SUCCESS -> {
Timber.i("Registration result: Success")
view?.openMainView()
logRegisterEvent(students)
logRegisterEvent(studentsWithSemesters)
}
Status.ERROR -> {
Timber.i("Registration result: An exception occurred ")
@ -119,7 +120,7 @@ class LoginStudentSelectPresenter @Inject constructor(
}
lastError = it.error
loginErrorHandler.dispatch(it.error!!)
logRegisterEvent(students, it.error)
logRegisterEvent(studentsWithSemesters, it.error)
}
}
}.launch("register")
@ -133,13 +134,13 @@ class LoginStudentSelectPresenter @Inject constructor(
view?.openEmail(lastError?.message.ifNullOrBlank { "empty" })
}
private fun logRegisterEvent(students: List<Student>, error: Throwable? = null) {
students.forEach { student ->
private fun logRegisterEvent(studentsWithSemesters: List<StudentWithSemesters>, error: Throwable? = null) {
studentsWithSemesters.forEach { student ->
analytics.logEvent(
"registration_student_select",
"success" to (error != null),
"scrapperBaseUrl" to student.scrapperBaseUrl,
"symbol" to student.symbol,
"scrapperBaseUrl" to student.student.scrapperBaseUrl,
"symbol" to student.student.symbol,
"error" to (error?.message?.ifBlank { "No message" } ?: "No error"))
}
}

View File

@ -1,13 +1,13 @@
package io.github.wulkanowy.ui.modules.login.studentselect
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface LoginStudentSelectView : BaseView {
fun initView()
fun updateData(data: List<Pair<Student, Boolean>>)
fun updateData(data: List<Pair<StudentWithSemesters, Boolean>>)
fun openMainView()

View File

@ -10,7 +10,7 @@ import android.widget.ArrayAdapter
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
@ -108,8 +108,8 @@ class LoginSymbolFragment :
binding.loginSymbolContainer.visibility = if (show) VISIBLE else GONE
}
override fun notifyParentAccountLogged(students: List<Student>) {
(activity as? LoginActivity)?.onSymbolFragmentAccountLogged(students)
override fun notifyParentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>) {
(activity as? LoginActivity)?.onSymbolFragmentAccountLogged(studentsWithSemesters)
}
override fun onSaveInstanceState(outState: Bundle) {

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.login.symbol
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface LoginSymbolView : BaseView {
@ -25,7 +25,7 @@ interface LoginSymbolView : BaseView {
fun showContent(show: Boolean)
fun notifyParentAccountLogged(students: List<Student>)
fun notifyParentAccountLogged(studentsWithSemesters: List<StudentWithSemesters>)
fun showContact(show: Boolean)

View File

@ -55,11 +55,11 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
when {
it.data!!.isEmpty() -> view?.openLoginView()
it.data.size == 1 -> {
selectedStudent = it.data.single()
selectedStudent = it.data.single().student
view?.showThemeDialog()
}
else -> view?.updateData(it.data.map { student ->
student to (student.id == widgetId)
else -> view?.updateData(it.data.map { entity ->
entity.student to (entity.student.id == widgetId)
})
}
}

View File

@ -128,7 +128,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking {
try {
val students = studentRepository.getSavedStudents()
val student = students.singleOrNull { student -> student.id == studentId }
val student = students.singleOrNull { it.student.id == studentId }?.student
val currentStudent = when {
student != null -> student
studentId != 0L && studentRepository.isCurrentStudentSet() -> {

View File

@ -206,7 +206,7 @@ class MessageTabPresenter @Inject constructor(
query.toLowerCase(Locale.getDefault()),
message.date.toFormattedString("d MMMM yyyy").toLowerCase(Locale.getDefault())
)
).max() ?: 0
).maxOrNull() ?: 0
return (subjectRatio.toDouble().pow(2)

View File

@ -10,7 +10,7 @@ import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.databinding.ItemNoteBinding
import io.github.wulkanowy.sdk.scrapper.notes.Note.CategoryType
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
@ -43,9 +43,9 @@ class NoteAdapter @Inject constructor() : RecyclerView.Adapter<NoteAdapter.ItemV
with(noteItemPoints) {
text = "${if (item.points > 0) "+" else ""}${item.points}"
visibility = if (item.isPointsShow) View.VISIBLE else View.GONE
setTextColor(when (CategoryType.getByValue(item.categoryType)) {
CategoryType.POSITIVE -> ContextCompat.getColor(context, R.color.note_positive)
CategoryType.NEGATIVE -> ContextCompat.getColor(context, R.color.note_negative)
setTextColor(when (NoteCategory.getByValue(item.categoryType)) {
NoteCategory.POSITIVE -> ContextCompat.getColor(context, R.color.note_positive)
NoteCategory.NEGATIVE -> ContextCompat.getColor(context, R.color.note_negative)
else -> context.getThemeAttrColor(android.R.attr.textColorPrimary)
})
}

View File

@ -10,7 +10,7 @@ import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.databinding.DialogNoteBinding
import io.github.wulkanowy.sdk.scrapper.notes.Note.CategoryType
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.toFormattedString
@ -57,9 +57,9 @@ class NoteDialog : DialogFragment() {
if (note.isPointsShow) {
with(binding.noteDialogPoints) {
text = "${if (note.points > 0) "+" else ""}${note.points}"
setTextColor(when (CategoryType.getByValue(note.categoryType)) {
CategoryType.POSITIVE -> ContextCompat.getColor(requireContext(), R.color.note_positive)
CategoryType.NEGATIVE -> ContextCompat.getColor(requireContext(), R.color.note_negative)
setTextColor(when (NoteCategory.getByValue(note.categoryType)) {
NoteCategory.POSITIVE -> ContextCompat.getColor(requireContext(), R.color.note_positive)
NoteCategory.NEGATIVE -> ContextCompat.getColor(requireContext(), R.color.note_negative)
else -> requireContext().getThemeAttrColor(android.R.attr.textColorPrimary)
})
}

View File

@ -60,11 +60,11 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
when {
it.data!!.isEmpty() -> view?.openLoginView()
it.data.size == 1 && !isFromProvider -> {
selectedStudent = it.data.single()
selectedStudent = it.data.single().student
view?.showThemeDialog()
}
else -> view?.updateData(it.data.map { student ->
student to (student.id == widgetId)
else -> view?.updateData(it.data.map { entity ->
entity.student to (entity.student.id == widgetId)
})
}
}

View File

@ -102,7 +102,7 @@ class TimetableWidgetFactory(
if (!studentRepository.isStudentSaved()) return@runBlocking emptyList<Timetable>()
val students = studentRepository.getSavedStudents()
val student = students.singleOrNull { student -> student.id == studentId }
val student = students.singleOrNull { it.student.id == studentId }?.student
?: return@runBlocking emptyList<Timetable>()
val semester = semesterRepository.getCurrentSemester(student)

View File

@ -185,7 +185,7 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try {
val students = studentRepository.getSavedStudents(false)
val student = students.singleOrNull { student -> student.id == studentId }
val student = students.singleOrNull { it -> it.student.id == studentId }?.student
when {
student != null -> student
studentId != 0L && studentRepository.isCurrentStudentSet() -> {

View File

@ -13,7 +13,7 @@ fun List<Semester>.getCurrentOrLast(): Semester {
singleOrNull { it.isCurrent }?.let { return it }
// when there is more than one current semester - find one with higher id
singleOrNull { semester -> semester.semesterId == maxBy { it.semesterId }?.semesterId }?.let { return it }
singleOrNull { semester -> semester.semesterId == maxByOrNull { it.semesterId }?.semesterId }?.let { return it }
throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}")
}

View File

@ -26,7 +26,7 @@ class StudentRemoteTest {
val students = runBlocking { StudentRemote(mockSdk).getStudentsScrapper("", "", "http://fakelog.cf", "") }
assertEquals(1, students.size)
assertEquals("test", students.first().studentName)
assertEquals("test Kowalski", students.first().student.studentName)
}
private fun getStudent(name: String): Student {
@ -35,7 +35,10 @@ class StudentRemoteTest {
symbol = "",
studentId = 0,
userLoginId = 0,
userLogin = "",
userName = "",
studentName = name,
studentSurname = "Kowalski",
schoolSymbol = "",
schoolShortName = "",
schoolName = "",
@ -47,7 +50,8 @@ class StudentRemoteTest {
mobileBaseUrl = "",
loginType = Sdk.ScrapperLoginType.STANDARD,
scrapperBaseUrl = "",
isParent = false
isParent = false,
semesters = emptyList()
)
}
}

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.MainCoroutineRule
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
@ -99,7 +100,7 @@ class LoginFormPresenterTest {
@Test
fun loginTest() {
val studentTest = Student(email = "test@", password = "123", scrapperBaseUrl = "https://fakelog.cf/", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "", mobileBaseUrl = "", privateKey = "", certificateKey = "", loginMode = "", userLoginId = 0, schoolShortName = "", isParent = false)
coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } returns listOf(studentTest)
coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } returns listOf(StudentWithSemesters(studentTest, emptyList()))
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.studentselect
import io.github.wulkanowy.MainCoroutineRule
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
@ -63,10 +64,10 @@ class LoginStudentSelectPresenterTest {
@Test
fun onSelectedStudentTest() {
coEvery { studentRepository.saveStudents(listOf(testStudent)) } returns listOf(1L)
coEvery { studentRepository.switchStudent(testStudent) } just Runs
coEvery { studentRepository.saveStudents(listOf(StudentWithSemesters(testStudent, emptyList()))) } returns listOf(1L)
coEvery { studentRepository.switchStudent(StudentWithSemesters(testStudent, emptyList())) } just Runs
every { loginStudentSelectView.openMainView() } just Runs
presenter.onItemSelected(testStudent, false)
presenter.onItemSelected(StudentWithSemesters(testStudent, emptyList()), false)
presenter.onSignIn()
verify { loginStudentSelectView.showContent(false) }
@ -76,9 +77,9 @@ class LoginStudentSelectPresenterTest {
@Test
fun onSelectedStudentErrorTest() {
coEvery { studentRepository.saveStudents(listOf(testStudent)) } throws testException
coEvery { studentRepository.saveStudents(listOf(StudentWithSemesters(testStudent, emptyList()))) } throws testException
coEvery { studentRepository.logoutStudent(testStudent) } just Runs
presenter.onItemSelected(testStudent, false)
presenter.onItemSelected(StudentWithSemesters(testStudent, emptyList()), false)
presenter.onSignIn()
verify { loginStudentSelectView.showContent(false) }
verify { loginStudentSelectView.showProgress(true) }

View File

@ -1,6 +1,6 @@
buildscript {
ext {
kotlin_version = '1.3.72'
kotlin_version = '1.4.0'
about_libraries = '8.3.0'
hilt_version = "2.28.3-alpha"
}