forked from github/wulkanowy-mirror
Fallback to subject name from timetable when attendance item doesn't have it (#2052)
* Fallback to subject name from timetable when attendance item doesn't have it * Fix tests * Add some unit tests
This commit is contained in:
parent
7a408899df
commit
6c115fb915
@ -49,8 +49,8 @@ fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
|||||||
|
|
||||||
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
||||||
val description = when (it) {
|
val description = when (it) {
|
||||||
is Resource.Loading -> "started"
|
|
||||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
||||||
|
is Resource.Loading -> "started"
|
||||||
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
|
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
|
||||||
is Resource.Error -> "exception occurred: ${it.error}"
|
is Resource.Error -> "exception occurred: ${it.error}"
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,7 @@ interface TimetableDao : BaseDao<Timetable> {
|
|||||||
|
|
||||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
|
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,22 @@ package io.github.wulkanowy.data.mappers
|
|||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
||||||
import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary
|
import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary
|
||||||
|
|
||||||
fun List<SdkAttendance>.mapToEntities(semester: Semester) = map {
|
fun List<SdkAttendance>.mapToEntities(semester: Semester, lessons: List<Timetable>) = map {
|
||||||
Attendance(
|
Attendance(
|
||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
timeId = it.timeId,
|
timeId = it.timeId,
|
||||||
number = it.number,
|
number = it.number,
|
||||||
subject = it.subject,
|
subject = it.subject.ifBlank {
|
||||||
|
lessons.find { lesson ->
|
||||||
|
lesson.date == it.date && lesson.number == it.number
|
||||||
|
}?.subject.orEmpty()
|
||||||
|
},
|
||||||
name = it.name,
|
name = it.name,
|
||||||
presence = it.presence,
|
presence = it.presence,
|
||||||
absence = it.absence,
|
absence = it.absence,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -9,8 +10,10 @@ import io.github.wulkanowy.data.networkBoundResource
|
|||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.sdk.pojo.Absent
|
import io.github.wulkanowy.sdk.pojo.Absent
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
@ -20,6 +23,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class AttendanceRepository @Inject constructor(
|
class AttendanceRepository @Inject constructor(
|
||||||
private val attendanceDb: AttendanceDao,
|
private val attendanceDb: AttendanceDao,
|
||||||
|
private val timetableDb: TimetableDao,
|
||||||
private val sdk: Sdk,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
@ -48,10 +52,15 @@ class AttendanceRepository @Inject constructor(
|
|||||||
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
|
val lessons = withContext(Dispatchers.IO) {
|
||||||
|
timetableDb.load(
|
||||||
|
semester.diaryId, semester.studentId, start.monday, end.sunday
|
||||||
|
)
|
||||||
|
}
|
||||||
sdk.init(student)
|
sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester, lessons)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
package io.github.wulkanowy.data.mappers
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
|
import io.github.wulkanowy.sdk.pojo.Attendance
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.attendance.SentExcuse
|
||||||
|
import org.junit.Test
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class AttendanceMapperTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `map attendance when fallback is not necessary`() {
|
||||||
|
val attendance = listOf(
|
||||||
|
getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"),
|
||||||
|
getSdkAttendance(2, LocalDate.of(2022, 11, 17), "Oryginalna 2"),
|
||||||
|
)
|
||||||
|
val lessons = listOf(
|
||||||
|
getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
|
||||||
|
getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = attendance.mapToEntities(getEntitySemester(), lessons)
|
||||||
|
assertEquals("Oryginalna 1", result[0].subject)
|
||||||
|
assertEquals("Oryginalna 2", result[1].subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `map attendance when fallback is not always necessary`() {
|
||||||
|
val attendance = listOf(
|
||||||
|
getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"),
|
||||||
|
getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""),
|
||||||
|
)
|
||||||
|
val lessons = listOf(
|
||||||
|
getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
|
||||||
|
getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = attendance.mapToEntities(getEntitySemester(), lessons)
|
||||||
|
assertEquals("Oryginalna 1", result[0].subject)
|
||||||
|
assertEquals("Druga", result[1].subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `map attendance when fallback is sometimes empty`() {
|
||||||
|
val attendance = listOf(
|
||||||
|
getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"),
|
||||||
|
getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""),
|
||||||
|
)
|
||||||
|
val lessons = listOf(
|
||||||
|
getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = attendance.mapToEntities(getEntitySemester(), lessons)
|
||||||
|
assertEquals("Oryginalna 1", result[0].subject)
|
||||||
|
assertEquals("", result[1].subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `map attendance when fallback is empty`() {
|
||||||
|
val attendance = listOf(
|
||||||
|
getSdkAttendance(1, LocalDate.of(2022, 11, 17), ""),
|
||||||
|
getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""),
|
||||||
|
)
|
||||||
|
val lessons = listOf(
|
||||||
|
getEntityTimetable(1, LocalDate.of(2022, 11, 18), "Pierwsza"),
|
||||||
|
getEntityTimetable(2, LocalDate.of(2022, 10, 17), "Druga"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = attendance.mapToEntities(getEntitySemester(), lessons)
|
||||||
|
assertEquals("", result[0].subject)
|
||||||
|
assertEquals("", result[1].subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `map attendance with all subject fallback`() {
|
||||||
|
val attendance = listOf(
|
||||||
|
getSdkAttendance(1, LocalDate.of(2022, 11, 17)),
|
||||||
|
getSdkAttendance(2, LocalDate.of(2022, 11, 17)),
|
||||||
|
)
|
||||||
|
val lessons = listOf(
|
||||||
|
getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
|
||||||
|
getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = attendance.mapToEntities(getEntitySemester(), lessons)
|
||||||
|
assertEquals("Pierwsza", result[0].subject)
|
||||||
|
assertEquals("Druga", result[1].subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSdkAttendance(number: Int, date: LocalDate, subject: String = "") = Attendance(
|
||||||
|
number = number,
|
||||||
|
name = "ABSENCE",
|
||||||
|
subject = subject,
|
||||||
|
date = date,
|
||||||
|
timeId = 1,
|
||||||
|
categoryId = 1,
|
||||||
|
deleted = false,
|
||||||
|
excuseStatus = SentExcuse.Status.WAITING,
|
||||||
|
excusable = false,
|
||||||
|
absence = false,
|
||||||
|
excused = false,
|
||||||
|
exemption = false,
|
||||||
|
lateness = false,
|
||||||
|
presence = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getEntityTimetable(number: Int, date: LocalDate, subject: String = "") = Timetable(
|
||||||
|
number = number,
|
||||||
|
start = Instant.now(),
|
||||||
|
end = Instant.now(),
|
||||||
|
date = date,
|
||||||
|
subject = subject,
|
||||||
|
subjectOld = "",
|
||||||
|
group = "",
|
||||||
|
room = "",
|
||||||
|
roomOld = "",
|
||||||
|
teacher = "",
|
||||||
|
teacherOld = "",
|
||||||
|
info = "",
|
||||||
|
changes = false,
|
||||||
|
canceled = false,
|
||||||
|
studentId = 0,
|
||||||
|
diaryId = 0,
|
||||||
|
isStudentPlan = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getEntitySemester() = Semester(
|
||||||
|
studentId = 0,
|
||||||
|
diaryId = 0,
|
||||||
|
kindergartenDiaryId = 0,
|
||||||
|
diaryName = "",
|
||||||
|
schoolYear = 0,
|
||||||
|
semesterId = 0,
|
||||||
|
semesterName = 0,
|
||||||
|
start = LocalDate.now(),
|
||||||
|
end = LocalDate.now(),
|
||||||
|
classId = 0,
|
||||||
|
unitId = 0
|
||||||
|
)
|
||||||
|
}
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories
|
|||||||
|
|
||||||
import io.github.wulkanowy.data.dataOrNull
|
import io.github.wulkanowy.data.dataOrNull
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.errorOrNull
|
import io.github.wulkanowy.data.errorOrNull
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.toFirstResult
|
import io.github.wulkanowy.data.toFirstResult
|
||||||
@ -29,6 +30,9 @@ class AttendanceRepositoryTest {
|
|||||||
@MockK
|
@MockK
|
||||||
private lateinit var attendanceDb: AttendanceDao
|
private lateinit var attendanceDb: AttendanceDao
|
||||||
|
|
||||||
|
@MockK
|
||||||
|
private lateinit var timetableDb: TimetableDao
|
||||||
|
|
||||||
@MockK(relaxUnitFun = true)
|
@MockK(relaxUnitFun = true)
|
||||||
private lateinit var refreshHelper: AutoRefreshHelper
|
private lateinit var refreshHelper: AutoRefreshHelper
|
||||||
|
|
||||||
@ -51,8 +55,9 @@ class AttendanceRepositoryTest {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockKAnnotations.init(this)
|
MockKAnnotations.init(this)
|
||||||
every { refreshHelper.shouldBeRefreshed(any()) } returns false
|
every { refreshHelper.shouldBeRefreshed(any()) } returns false
|
||||||
|
coEvery { timetableDb.load(any(), any(), any(), any()) } returns emptyList()
|
||||||
|
|
||||||
attendanceRepository = AttendanceRepository(attendanceDb, sdk, refreshHelper)
|
attendanceRepository = AttendanceRepository(attendanceDb, timetableDb, sdk, refreshHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -60,8 +65,8 @@ class AttendanceRepositoryTest {
|
|||||||
// prepare
|
// prepare
|
||||||
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
|
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
|
||||||
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
||||||
flowOf(remoteList.mapToEntities(semester)),
|
flowOf(remoteList.mapToEntities(semester, emptyList())),
|
||||||
flowOf(remoteList.mapToEntities(semester))
|
flowOf(remoteList.mapToEntities(semester, emptyList()))
|
||||||
)
|
)
|
||||||
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
|
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
|
||||||
coEvery { attendanceDb.deleteAll(any()) } just Runs
|
coEvery { attendanceDb.deleteAll(any()) } just Runs
|
||||||
@ -83,9 +88,9 @@ class AttendanceRepositoryTest {
|
|||||||
// prepare
|
// prepare
|
||||||
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
|
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
|
||||||
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
||||||
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
|
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())),
|
||||||
flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
|
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result
|
||||||
flowOf(remoteList.mapToEntities(semester))
|
flowOf(remoteList.mapToEntities(semester, emptyList()))
|
||||||
)
|
)
|
||||||
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
|
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
|
||||||
coEvery { attendanceDb.deleteAll(any()) } just Runs
|
coEvery { attendanceDb.deleteAll(any()) } just Runs
|
||||||
@ -100,7 +105,7 @@ class AttendanceRepositoryTest {
|
|||||||
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
|
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
|
||||||
coVerify {
|
coVerify {
|
||||||
attendanceDb.insertAll(match {
|
attendanceDb.insertAll(match {
|
||||||
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
|
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
|
coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
|
||||||
@ -111,9 +116,9 @@ class AttendanceRepositoryTest {
|
|||||||
// prepare
|
// prepare
|
||||||
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1)
|
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1)
|
||||||
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
||||||
flowOf(remoteList.mapToEntities(semester)),
|
flowOf(remoteList.mapToEntities(semester, emptyList())),
|
||||||
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
|
flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result
|
||||||
flowOf(remoteList.dropLast(1).mapToEntities(semester))
|
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList()))
|
||||||
)
|
)
|
||||||
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
|
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
|
||||||
coEvery { attendanceDb.deleteAll(any()) } just Runs
|
coEvery { attendanceDb.deleteAll(any()) } just Runs
|
||||||
@ -129,7 +134,7 @@ class AttendanceRepositoryTest {
|
|||||||
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
|
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
|
||||||
coVerify {
|
coVerify {
|
||||||
attendanceDb.deleteAll(match {
|
attendanceDb.deleteAll(match {
|
||||||
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
|
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user