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

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:
Mikołaj Pich 2022-11-18 16:32:04 +01:00 committed by GitHub
parent 7a408899df
commit 6c115fb915
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 15 deletions

View File

@ -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 {
val description = when (it) {
is Resource.Loading -> "started"
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.Error -> "exception occurred: ${it.error}"
}

View File

@ -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")
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>
}

View File

@ -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.AttendanceSummary
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.AttendanceSummary as SdkAttendanceSummary
fun List<SdkAttendance>.mapToEntities(semester: Semester) = map {
fun List<SdkAttendance>.mapToEntities(semester: Semester, lessons: List<Timetable>) = map {
Attendance(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date,
timeId = it.timeId,
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,
presence = it.presence,
absence = it.absence,

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data.repositories
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.Semester
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.pojo.Absent
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
@ -20,6 +23,7 @@ import javax.inject.Singleton
@Singleton
class AttendanceRepository @Inject constructor(
private val attendanceDb: AttendanceDao,
private val timetableDb: TimetableDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
@ -48,10 +52,15 @@ class AttendanceRepository @Inject constructor(
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
},
fetch = {
val lessons = withContext(Dispatchers.IO) {
timetableDb.load(
semester.diaryId, semester.studentId, start.monday, end.sunday
)
}
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getAttendance(start.monday, end.sunday, semester.semesterId)
.mapToEntities(semester)
.mapToEntities(semester, lessons)
},
saveFetchResult = { old, new ->
attendanceDb.deleteAll(old uniqueSubtract new)

View File

@ -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
)
}

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.dataOrNull
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.mappers.mapToEntities
import io.github.wulkanowy.data.toFirstResult
@ -29,6 +30,9 @@ class AttendanceRepositoryTest {
@MockK
private lateinit var attendanceDb: AttendanceDao
@MockK
private lateinit var timetableDb: TimetableDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
@ -51,8 +55,9 @@ class AttendanceRepositoryTest {
fun setUp() {
MockKAnnotations.init(this)
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
@ -60,8 +65,8 @@ class AttendanceRepositoryTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
@ -83,9 +88,9 @@ class AttendanceRepositoryTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())),
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
@ -100,7 +105,7 @@ class AttendanceRepositoryTest {
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify {
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() }) }
@ -111,9 +116,9 @@ class AttendanceRepositoryTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1)
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
@ -129,7 +134,7 @@ class AttendanceRepositoryTest {
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify {
attendanceDb.deleteAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
})
}
}