[API/Edudziennik] Add getting attendance.

This commit is contained in:
Kacper Ziubryniewicz 2019-12-24 15:39:22 +01:00
parent 5ca8b642da
commit 60ad2e81f3
5 changed files with 146 additions and 26 deletions

View File

@ -4,6 +4,8 @@
package pl.szczodrzynski.edziennik.data.api package pl.szczodrzynski.edziennik.data.api
import kotlin.text.RegexOption.DOT_MATCHES_ALL
object Regexes { object Regexes {
val STYLE_CSS_COLOR by lazy { val STYLE_CSS_COLOR by lazy {
"""color: \w+?;?"?""".toRegex() """color: \w+?;?"?""".toRegex()
@ -12,69 +14,69 @@ object Regexes {
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy { val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
"""<div.*?>\n*\s*(.+?)\s*\n*(?:<.*?)??</div>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<div.*?>\n*\s*(.+?)\s*\n*(?:<.*?)??</div>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_COLOR by lazy { val MOBIDZIENNIK_GRADES_COLOR by lazy {
"""background-color:([#A-Fa-f0-9]+);""".toRegex(RegexOption.DOT_MATCHES_ALL) """background-color:([#A-Fa-f0-9]+);""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_CATEGORY by lazy { val MOBIDZIENNIK_GRADES_CATEGORY by lazy {
""">&nbsp;(.+?):</span>""".toRegex(RegexOption.DOT_MATCHES_ALL) """>&nbsp;(.+?):</span>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_CLASS_AVERAGE by lazy { val MOBIDZIENNIK_GRADES_CLASS_AVERAGE by lazy {
"""Średnia ocen:.*<strong>([0-9]*\.?[0-9]*)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL) """Średnia ocen:.*<strong>([0-9]*\.?[0-9]*)</strong>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_ADDED_DATE by lazy { val MOBIDZIENNIK_GRADES_ADDED_DATE by lazy {
"""Wpisano:.*<strong>.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL) """Wpisano:.*<strong>.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)</strong>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_COUNT_TO_AVG by lazy { val MOBIDZIENNIK_GRADES_COUNT_TO_AVG by lazy {
"""Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(RegexOption.DOT_MATCHES_ALL) """Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_DETAILS by lazy { val MOBIDZIENNIK_GRADES_DETAILS by lazy {
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_EVENT_TYPE by lazy { val MOBIDZIENNIK_EVENT_TYPE by lazy {
"""\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(RegexOption.DOT_MATCHES_ALL) """\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_LUCKY_NUMBER by lazy { val MOBIDZIENNIK_LUCKY_NUMBER by lazy {
"""class="szczesliwy_numerek".*>0*([0-9]+)(?:/0*[0-9]+)*</a>""".toRegex(RegexOption.DOT_MATCHES_ALL) """class="szczesliwy_numerek".*>0*([0-9]+)(?:/0*[0-9]+)*</a>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_CLASS_CALENDAR by lazy { val MOBIDZIENNIK_CLASS_CALENDAR by lazy {
"""events: (.+),$""".toRegex(RegexOption.MULTILINE) """events: (.+),$""".toRegex(RegexOption.MULTILINE)
} }
val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy { val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy {
"""czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL) """czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy { val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
""".+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL) """.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy { val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy {
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(RegexOption.DOT_MATCHES_ALL) """href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy { val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
"""<input type="hidden".+?name="([A-z0-9_]+)?".+?value="([A-z0-9_+-/=]+)?".+?>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<input type="hidden".+?name="([A-z0-9_]+)?".+?value="([A-z0-9_+-/=]+)?".+?>""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_ERROR by lazy { val IDZIENNIK_LOGIN_ERROR by lazy {
"""id="spanErrorMessage">(.*?)</""".toRegex(RegexOption.DOT_MATCHES_ALL) """id="spanErrorMessage">(.*?)</""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME by lazy { val IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME by lazy {
"""Imię i nazwisko:.+?">(.+?)</div>""".toRegex(RegexOption.DOT_MATCHES_ALL) """Imię i nazwisko:.+?">(.+?)</div>""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_FIRST_IS_PARENT by lazy { val IDZIENNIK_LOGIN_FIRST_IS_PARENT by lazy {
"""id="ctl00_CzyRodzic" value="([01])" />""".toRegex() """id="ctl00_CzyRodzic" value="([01])" />""".toRegex()
} }
val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy { val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy {
"""name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9/]+)<""".toRegex(RegexOption.DOT_MATCHES_ALL) """name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9/]+)<""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy { val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy { val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy {
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(DOT_MATCHES_ALL)
} }
@ -98,6 +100,16 @@ object Regexes {
"""<span id='user_dn'>(.*?)</span>""".toRegex() """<span id='user_dn'>(.*?)</span>""".toRegex()
} }
val EDUDZIENNIK_ATTENDANCE_ENTRIES by lazy {
"""<td id="([\d-]+?):(\d+?)".*?>(.+?)</td>""".toRegex()
}
val EDUDZIENNIK_ATTENDANCE_TYPES by lazy {
"""<div class="info">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_ATTENDANCE_TYPE by lazy {
"""\((.+?)\) (.+)""".toRegex()
}
val EDUDZIENNIK_SUBJECT_ID by lazy { val EDUDZIENNIK_SUBJECT_ID by lazy {
"""/Courses/([\w-_]+?)/""".toRegex() """/Courses/([\w-_]+?)/""".toRegex()
} }
@ -112,15 +124,15 @@ object Regexes {
} }
val EDUDZIENNIK_SCHOOL_DETAIL_ID by lazy { val EDUDZIENNIK_SCHOOL_DETAIL_ID by lazy {
"""<a id="School_detail".*?/School/([\w-_]+?)/""".toRegex(RegexOption.DOT_MATCHES_ALL) """<a id="School_detail".*?/School/([\w-_]+?)/""".toRegex(DOT_MATCHES_ALL)
} }
val EDUDZIENNIK_SCHOOL_DETAIL_NAME by lazy { val EDUDZIENNIK_SCHOOL_DETAIL_NAME by lazy {
"""</li>.*?<p>(.*?)</p>.*?<li>""".toRegex(RegexOption.DOT_MATCHES_ALL) """</li>.*?<p>(.*?)</p>.*?<li>""".toRegex(DOT_MATCHES_ALL)
} }
val EDUDZIENNIK_CLASS_DETAIL_ID by lazy { val EDUDZIENNIK_CLASS_DETAIL_ID by lazy {
"""<a id="Klass_detail".*?/Klass/([\w-_]+?)/""".toRegex(RegexOption.DOT_MATCHES_ALL) """<a id="Klass_detail".*?/Klass/([\w-_]+?)/""".toRegex(DOT_MATCHES_ALL)
} }
val EDUDZIENNIK_CLASS_DETAIL_NAME by lazy { val EDUDZIENNIK_CLASS_DETAIL_NAME by lazy {
"""<a id="Klass_detail".*?>Klasa (.*?)</a>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<a id="Klass_detail".*?>Klasa (.*?)</a>""".toRegex(DOT_MATCHES_ALL)
} }
} }

View File

@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.models.Feature
const val ENDPOINT_EDUDZIENNIK_WEB_START = 1000 const val ENDPOINT_EDUDZIENNIK_WEB_START = 1000
const val ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE = 1001 const val ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE = 1001
const val ENDPOINT_EDUDZIENNIK_WEB_EXAMS = 1002 const val ENDPOINT_EDUDZIENNIK_WEB_EXAMS = 1002
const val ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE = 1003
const val ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER = 1010 const val ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER = 1010
val EdudziennikFeatures = listOf( val EdudziennikFeatures = listOf(
@ -29,6 +30,10 @@ val EdudziennikFeatures = listOf(
ENDPOINT_EDUDZIENNIK_WEB_EXAMS to LOGIN_METHOD_EDUDZIENNIK_WEB ENDPOINT_EDUDZIENNIK_WEB_EXAMS to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)), ), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_ATTENDANCE, listOf(
ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_LUCKY_NUMBER, listOf( Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER to LOGIN_METHOD_EDUDZIENNIK_WEB ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)) ), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB))

View File

@ -6,10 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.* import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebExams import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebLuckyNumber
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebStart
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebTimetable
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) { class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
@ -51,6 +48,10 @@ class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_exams) data.startProgress(R.string.edziennik_progress_endpoint_exams)
EdudziennikWebExams(data, onSuccess) EdudziennikWebExams(data, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
EdudziennikWebAttendance(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> { ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
EdudziennikWebLuckyNumber(data, onSuccess) EdudziennikWebLuckyNumber(data, onSuccess)

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-24
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_ENTRIES
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_TYPE
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
class EdudziennikWebAttendance(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
companion object {
private const val TAG = "EdudziennikWebAttendance"
}
init { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "Presence") { text ->
val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map {
val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim())
val symbol = type?.get(1)?.trim()
val name = type?.get(2)?.trim()
return@map Triple(
symbol,
name,
when (name?.toLowerCase(Locale.ROOT)) {
"obecność" -> Attendance.TYPE_PRESENT
"nieobecność" -> Attendance.TYPE_ABSENT
"spóźnienie" -> Attendance.TYPE_BELATED
"nieobecność usprawiedliwiona" -> Attendance.TYPE_ABSENT_EXCUSED
"dzień wolny" -> Attendance.TYPE_RELEASED
"brak zajęć" -> Attendance.TYPE_RELEASED
"oddelegowany" -> Attendance.TYPE_RELEASED
else -> Attendance.TYPE_CUSTOM
}
)
} ?: emptyList()
EDUDZIENNIK_ATTENDANCE_ENTRIES.findAll(text).forEach { attendanceElement ->
val date = Date.fromY_m_d(attendanceElement[1])
val lessonNumber = attendanceElement[2].toInt()
val attendanceSymbol = attendanceElement[3]
val lessons = data.app.db.timetableDao().getForDateNow(profileId, date)
val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber }
val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32()
val (_, name, type) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol }
?: return@forEach
val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime
?: return@forEach
val attendanceObject = Attendance(
profileId,
id,
lesson?.displayTeacherId ?: -1,
lesson?.displaySubjectId ?: -1,
profile.currentSemester,
name,
date,
lesson?.displayStartTime ?: startTime,
type
)
data.attendanceList.add(attendanceObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
id,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
onSuccess()
}
} ?: onSuccess() }
}

View File

@ -13,10 +13,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.Edudzienni
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.splitName import pl.szczodrzynski.edziennik.splitName
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -70,6 +72,12 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
val startTime = Time.fromH_m(times[0].trim()) val startTime = Time.fromH_m(times[0].trim())
val endTime = Time.fromH_m(times[1].trim()) val endTime = Time.fromH_m(times[1].trim())
data.lessonRanges.singleOrNull {
it.lessonNumber == lessonNumber && it.startTime == startTime && it.endTime == endTime
} ?: run {
data.lessonRanges.put(lessonNumber, LessonRange(profileId, lessonNumber, startTime, endTime))
}
rowElements.subList(2, rowElements.size).forEachIndexed { index, lesson -> rowElements.subList(2, rowElements.size).forEachIndexed { index, lesson ->
val course = lesson.select(".course").firstOrNull() ?: return@forEachIndexed val course = lesson.select(".course").firstOrNull() ?: return@forEachIndexed
val info = course.select("span > span") val info = course.select("span > span")