Try to fix attendance when eduOne is on
This commit is contained in:
parent
0bba02045f
commit
cd68beb772
11 changed files with 95 additions and 56 deletions
|
@ -16,6 +16,7 @@ import io.github.wulkanowy.sdk.scrapper.home.GovernmentUnit
|
|||
import io.github.wulkanowy.sdk.scrapper.home.LuckyNumber
|
||||
import io.github.wulkanowy.sdk.scrapper.homework.Homework
|
||||
import io.github.wulkanowy.sdk.scrapper.login.LoginHelper
|
||||
import io.github.wulkanowy.sdk.scrapper.login.UrlGenerator
|
||||
import io.github.wulkanowy.sdk.scrapper.menu.Menu
|
||||
import io.github.wulkanowy.sdk.scrapper.messages.Folder
|
||||
import io.github.wulkanowy.sdk.scrapper.messages.Mailbox
|
||||
|
@ -33,6 +34,7 @@ import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository
|
|||
import io.github.wulkanowy.sdk.scrapper.repository.HomepageRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.MessagesRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.RegisterRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.StudentPlusRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.StudentRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.StudentStartRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.school.School
|
||||
|
@ -65,6 +67,8 @@ class Scrapper {
|
|||
|
||||
private val cookieJarCabinet = CookieJarCabinet()
|
||||
|
||||
private var isEduOne = false
|
||||
|
||||
var logLevel: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BASIC
|
||||
set(value) {
|
||||
if (field != value) changeManager.reset()
|
||||
|
@ -237,6 +241,9 @@ class Scrapper {
|
|||
buildTag = buildTag,
|
||||
emptyCookieJarIntercept = emptyCookieJarInterceptor,
|
||||
userAgentTemplate = userAgentTemplate,
|
||||
onUserLoggedIn = { studentModuleUrls ->
|
||||
isEduOne = isCurrentLoginHasEduOne(studentModuleUrls)
|
||||
},
|
||||
).apply {
|
||||
appInterceptors.forEach { (interceptor, isNetwork) ->
|
||||
setInterceptor(interceptor, isNetwork)
|
||||
|
@ -244,6 +251,15 @@ class Scrapper {
|
|||
}
|
||||
}
|
||||
|
||||
private fun isCurrentLoginHasEduOne(studentModuleUrls: List<String>): Boolean {
|
||||
return studentModuleUrls.any {
|
||||
it.startsWith(
|
||||
prefix = serviceManager.urlGenerator.generate(UrlGenerator.Site.STUDENT_PLUS),
|
||||
ignoreCase = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val account by lazy { AccountRepository(serviceManager.getAccountService()) }
|
||||
|
||||
private val register by resettableLazy(changeManager) {
|
||||
|
@ -281,7 +297,12 @@ class Scrapper {
|
|||
private val student: StudentRepository by resettableLazy(changeManager) {
|
||||
StudentRepository(
|
||||
api = serviceManager.getStudentService(),
|
||||
studentPlusService = serviceManager.getStudentPlusService(),
|
||||
)
|
||||
}
|
||||
|
||||
private val studentPlus: StudentPlusRepository by resettableLazy(changeManager) {
|
||||
StudentPlusRepository(
|
||||
api = serviceManager.getStudentPlusService(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -316,7 +337,10 @@ class Scrapper {
|
|||
suspend fun getAttendance(startDate: LocalDate, endDate: LocalDate? = null): List<Attendance> {
|
||||
if (diaryId == 0) return emptyList()
|
||||
|
||||
return student.getAttendance(startDate, endDate, studentId, diaryId)
|
||||
return when (isEduOne) {
|
||||
true -> studentPlus.getAttendance(startDate, endDate, studentId, diaryId)
|
||||
else -> student.getAttendance(startDate, endDate)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getAttendanceSummary(subjectId: Int? = -1): List<AttendanceSummary> {
|
||||
|
@ -396,7 +420,10 @@ class Scrapper {
|
|||
suspend fun getCompletedLessons(startDate: LocalDate, endDate: LocalDate? = null, subjectId: Int = -1): List<CompletedLesson> {
|
||||
if (diaryId == 0) return emptyList()
|
||||
|
||||
return student.getCompletedLessons(startDate, endDate, subjectId)
|
||||
return when (isEduOne) {
|
||||
true -> studentPlus.getCompletedLessons()
|
||||
else -> student.getCompletedLessons(startDate, endDate, subjectId)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getRegisteredDevices(): List<Device> = student.getRegisteredDevices()
|
||||
|
|
|
@ -48,6 +48,7 @@ internal class AutoLoginInterceptor(
|
|||
private val notLoggedInCallback: suspend () -> HomePageResponse,
|
||||
private val fetchStudentCookies: () -> Pair<HttpUrl, Document>,
|
||||
private val fetchMessagesCookies: () -> Pair<HttpUrl, Document>,
|
||||
private val onUserLoggedIn: (studentModuleUrls: List<String>) -> Unit = {},
|
||||
) : Interceptor {
|
||||
|
||||
companion object {
|
||||
|
@ -84,8 +85,7 @@ internal class AutoLoginInterceptor(
|
|||
try {
|
||||
val homePageResponse = runBlocking { notLoggedInCallback() }
|
||||
val studentModuleUrls = homePageResponse.studentSchools.map { it.attr("href") }
|
||||
|
||||
logger.debug("Found student module urls: {}", studentModuleUrls)
|
||||
onUserLoggedIn(studentModuleUrls)
|
||||
studentModuleHeaders = null
|
||||
messagesModuleHeaders = null
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package io.github.wulkanowy.sdk.scrapper.repository
|
||||
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.Attendance
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceCategory
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||
import io.github.wulkanowy.sdk.scrapper.service.StudentPlusService
|
||||
import io.github.wulkanowy.sdk.scrapper.timetable.CacheEduOneResponse
|
||||
import io.github.wulkanowy.sdk.scrapper.timetable.CompletedLesson
|
||||
import io.github.wulkanowy.sdk.scrapper.toFormat
|
||||
import java.time.LocalDate
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
|
||||
internal class StudentPlusRepository(
|
||||
private val api: StudentPlusService,
|
||||
) {
|
||||
|
||||
private fun LocalDate.toISOFormat(): String = toFormat("yyyy-MM-dd'T00:00:00'")
|
||||
|
||||
private suspend fun getCache(): CacheEduOneResponse {
|
||||
return api.getUserCache()
|
||||
}
|
||||
|
||||
suspend fun getAttendance(startDate: LocalDate, endDate: LocalDate?, studentId: Int, diaryId: Int): List<Attendance> {
|
||||
return api.getAttendance(
|
||||
key = getEncodedKey(studentId, diaryId),
|
||||
from = startDate.toISOFormat(),
|
||||
to = endDate?.toISOFormat() ?: startDate.plusDays(7).toISOFormat(),
|
||||
).onEach {
|
||||
it.category = AttendanceCategory.getCategoryById(it.categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getCompletedLessons(): List<CompletedLesson> {
|
||||
val cache = getCache()
|
||||
if (!cache.showCompletedLessons) throw FeatureDisabledException("Widok lekcji zrealizowanych został wyłączony przez Administratora szkoły")
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
private fun getEncodedKey(studentId: Int, diaryId: Int): String {
|
||||
return Base64.encode("$studentId-$diaryId-1".toByteArray())
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package io.github.wulkanowy.sdk.scrapper.repository
|
|||
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.Absent
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.Attendance
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceCategory
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceExcuseRequest
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceRequest
|
||||
import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceSummary
|
||||
|
@ -17,7 +16,6 @@ import io.github.wulkanowy.sdk.scrapper.exams.ExamRequest
|
|||
import io.github.wulkanowy.sdk.scrapper.exams.mapExamsList
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||
import io.github.wulkanowy.sdk.scrapper.getSchoolYear
|
||||
import io.github.wulkanowy.sdk.scrapper.getScriptFlag
|
||||
import io.github.wulkanowy.sdk.scrapper.grades.GradePointsSummary
|
||||
import io.github.wulkanowy.sdk.scrapper.grades.GradeRequest
|
||||
import io.github.wulkanowy.sdk.scrapper.grades.Grades
|
||||
|
@ -46,7 +44,6 @@ import io.github.wulkanowy.sdk.scrapper.school.School
|
|||
import io.github.wulkanowy.sdk.scrapper.school.Teacher
|
||||
import io.github.wulkanowy.sdk.scrapper.school.mapToSchool
|
||||
import io.github.wulkanowy.sdk.scrapper.school.mapToTeachers
|
||||
import io.github.wulkanowy.sdk.scrapper.service.StudentPlusService
|
||||
import io.github.wulkanowy.sdk.scrapper.service.StudentService
|
||||
import io.github.wulkanowy.sdk.scrapper.student.StudentInfo
|
||||
import io.github.wulkanowy.sdk.scrapper.student.StudentPhoto
|
||||
|
@ -62,31 +59,17 @@ import io.github.wulkanowy.sdk.scrapper.timetable.mapTimetableList
|
|||
import io.github.wulkanowy.sdk.scrapper.toFormat
|
||||
import org.jsoup.Jsoup
|
||||
import java.time.LocalDate
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
|
||||
internal class StudentRepository(
|
||||
private val api: StudentService,
|
||||
private val studentPlusService: StudentPlusService,
|
||||
) {
|
||||
|
||||
private var isEduOne: Boolean = false
|
||||
|
||||
private fun LocalDate.toISOFormat(): String = toFormat("yyyy-MM-dd'T00:00:00'")
|
||||
|
||||
private suspend fun getCache(): CacheResponse {
|
||||
if (isEduOne) error("Cache unavailable in eduOne compatibility mode")
|
||||
val startPage = getStartPage()
|
||||
|
||||
isEduOne = getScriptFlag("isEduOne", startPage)
|
||||
if (isEduOne) error("Unsupported eduOne detected!")
|
||||
|
||||
val res = api.getUserCache().handleErrors()
|
||||
|
||||
val data = requireNotNull(res.data) {
|
||||
"Required value was null. $res"
|
||||
return api.getUserCache().handleErrors().let {
|
||||
requireNotNull(it.data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
suspend fun authorizePermission(pesel: String): Boolean {
|
||||
|
@ -105,18 +88,8 @@ internal class StudentRepository(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
suspend fun getAttendance(startDate: LocalDate, endDate: LocalDate?, studentId: Int, diaryId: Int): List<Attendance> {
|
||||
suspend fun getAttendance(startDate: LocalDate, endDate: LocalDate?): List<Attendance> {
|
||||
val lessonTimes = runCatching { getCache().times }
|
||||
if (lessonTimes.isFailure && isEduOne) {
|
||||
return studentPlusService.getAttendance(
|
||||
key = Base64.encode("$studentId-$diaryId-1".toByteArray()),
|
||||
from = startDate.toISOFormat(),
|
||||
to = endDate?.toISOFormat() ?: startDate.plusDays(7).toISOFormat(),
|
||||
).onEach {
|
||||
it.category = AttendanceCategory.getCategoryById(it.categoryId)
|
||||
}
|
||||
}
|
||||
return api.getAttendance(AttendanceRequest(startDate.atStartOfDay()))
|
||||
.handleErrors()
|
||||
.data?.mapAttendanceList(startDate, endDate, lessonTimes.getOrThrow()).orEmpty()
|
||||
|
@ -279,8 +252,4 @@ internal class StudentRepository(
|
|||
UnregisterDeviceRequest(id),
|
||||
).handleErrors().success
|
||||
}
|
||||
|
||||
private suspend fun getStartPage(): String {
|
||||
return api.getStart()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ internal class ServiceManager(
|
|||
private val diaryId: Int,
|
||||
private val kindergartenDiaryId: Int,
|
||||
private val schoolYear: Int,
|
||||
onUserLoggedIn: (studentModuleUrls: List<String>) -> Unit = {},
|
||||
emptyCookieJarIntercept: Boolean,
|
||||
androidVersion: String,
|
||||
buildTag: String,
|
||||
|
@ -101,6 +102,7 @@ internal class ServiceManager(
|
|||
notLoggedInCallback = { loginHelper.login(email, password) },
|
||||
fetchStudentCookies = { loginHelper.loginStudent() },
|
||||
fetchMessagesCookies = { loginHelper.loginMessages() },
|
||||
onUserLoggedIn = onUserLoggedIn,
|
||||
) to false,
|
||||
UserAgentInterceptor(androidVersion, buildTag, userAgentTemplate) to false,
|
||||
HttpErrorInterceptor() to false,
|
||||
|
|
|
@ -3,18 +3,14 @@ package io.github.wulkanowy.sdk.scrapper.service
|
|||
import io.github.wulkanowy.sdk.scrapper.attendance.Attendance
|
||||
import io.github.wulkanowy.sdk.scrapper.conferences.Conference
|
||||
import io.github.wulkanowy.sdk.scrapper.mobile.Device
|
||||
import io.github.wulkanowy.sdk.scrapper.timetable.CacheResponse
|
||||
import io.github.wulkanowy.sdk.scrapper.timetable.CacheEduOneResponse
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Url
|
||||
|
||||
internal interface StudentPlusService {
|
||||
|
||||
@GET
|
||||
suspend fun getStart(@Url url: String): String
|
||||
|
||||
@GET("api/Cache")
|
||||
suspend fun getUserCache(): CacheResponse
|
||||
suspend fun getUserCache(): CacheEduOneResponse
|
||||
|
||||
@GET("api/Frekwencja")
|
||||
suspend fun getAttendance(
|
||||
|
|
|
@ -47,9 +47,6 @@ import retrofit2.http.Url
|
|||
|
||||
internal interface StudentService {
|
||||
|
||||
@GET("LoginEndpoint.aspx")
|
||||
suspend fun getStart(): String
|
||||
|
||||
@GET
|
||||
suspend fun getStart(@Url url: String): String
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package io.github.wulkanowy.sdk.scrapper.timetable
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal data class CacheEduOneResponse(
|
||||
@SerialName("isPokazLekcjeZrealizowaneOn")
|
||||
val showCompletedLessons: Boolean,
|
||||
)
|
|
@ -10,7 +10,6 @@ import io.github.wulkanowy.sdk.scrapper.login.UrlGenerator
|
|||
import io.github.wulkanowy.sdk.scrapper.register.HomePageResponse
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.StudentRepository
|
||||
import io.github.wulkanowy.sdk.scrapper.service.LoginService
|
||||
import io.github.wulkanowy.sdk.scrapper.service.StudentPlusService
|
||||
import io.github.wulkanowy.sdk.scrapper.service.StudentService
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
|
@ -56,7 +55,6 @@ abstract class BaseLocalTest : BaseTest() {
|
|||
val okHttp = getOkHttp(errorInterceptor = true, autoLoginInterceptorOn = true, loginType = loginType, autoLogin = autoLogin)
|
||||
return StudentRepository(
|
||||
api = getService(StudentService::class.java, server.url("/").toString(), false, okHttp),
|
||||
studentPlusService = getService(StudentPlusService::class.java, server.url("/").toString(), false, okHttp),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,10 @@ class AttendanceTest : BaseLocalTest() {
|
|||
|
||||
private val student by lazy {
|
||||
val repo = getStudentRepo {
|
||||
it.enqueue("WitrynaUcznia.html", RegisterTest::class.java)
|
||||
it.enqueue("UczenCache.json", RegisterTest::class.java)
|
||||
it.enqueue("Frekwencja.json", AttendanceTest::class.java)
|
||||
}
|
||||
runBlocking { repo.getAttendance(getLocalDate(2018, 10, 1), null, 1, 1) }
|
||||
runBlocking { repo.getAttendance(getLocalDate(2018, 10, 1), null) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -158,13 +157,11 @@ class AttendanceTest : BaseLocalTest() {
|
|||
@Test
|
||||
fun getAttendance_requestDateFormat() = runTest {
|
||||
val repo = getStudentRepo {
|
||||
it.enqueue("WitrynaUcznia.html", RegisterTest::class.java)
|
||||
it.enqueue("UczenCache.json", RegisterTest::class.java)
|
||||
it.enqueue("Frekwencja.json", AttendanceTest::class.java)
|
||||
}
|
||||
repo.getAttendance(getLocalDate(2018, 10, 1), null, 1, 2)
|
||||
repo.getAttendance(getLocalDate(2018, 10, 1), null)
|
||||
|
||||
server.takeRequest()
|
||||
server.takeRequest()
|
||||
val request = server.takeRequest()
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ class CompletedLessonsTest : BaseLocalTest() {
|
|||
|
||||
@Before
|
||||
fun setUp() {
|
||||
server.enqueue("WitrynaUcznia.html", RegisterTest::class.java)
|
||||
server.enqueue("UczenCache.json", RegisterTest::class.java)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue