mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2024-11-24 10:54:36 -06:00
[API/Mobidziennik] Add Web timetable scrapper. (#66)
* [API] Add utils for getting teacher, subject and team by name. * [API/Mobidziennik] Add Web timetable scrapper. * [API/Mobidziennik] Add missing Regexes.
This commit is contained in:
parent
9fdee6e0c7
commit
dd6a2c0979
@ -117,6 +117,17 @@ object Regexes {
|
||||
}
|
||||
|
||||
|
||||
val MOBIDZIENNIK_TIMETABLE_TOP by lazy {
|
||||
"""<div class="plansc_top">.+?</div></div>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_TIMETABLE_CELL by lazy {
|
||||
"""<div class="plansc_cnt_w" style="(.+?)">.+?style="(.+?)".+?title="(.+?)".+?>\s+(.+?)\s+</div>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_TIMETABLE_LEFT by lazy {
|
||||
"""<div class="plansc_godz">.+?</div></div>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
|
||||
"""<input type="hidden".+?name="([A-z0-9_]+)?".+?value="([A-z0-9_+-/=]+)?".+?>""".toRegex(DOT_MATCHES_ALL)
|
||||
|
@ -111,37 +111,6 @@ class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Dat
|
||||
val courseStudentEndpoint: String
|
||||
get() = "Course/$studentId/"
|
||||
|
||||
fun getSubject(longId: String, name: String): Subject {
|
||||
val id = longId.crc32()
|
||||
return subjectList.singleOrNull { it.id == id } ?: run {
|
||||
val subject = Subject(profileId, id, name, name)
|
||||
subjectList.put(id, subject)
|
||||
subject
|
||||
}
|
||||
}
|
||||
|
||||
fun getTeacher(firstName: String, lastName: String, longId: String? = null): Teacher {
|
||||
val name = "$firstName $lastName".fixName()
|
||||
val id = name.crc32()
|
||||
return teacherList.singleOrNull { it.id == id }?.also {
|
||||
if (longId != null && it.loginId == null) it.loginId = longId
|
||||
} ?: run {
|
||||
val teacher = Teacher(profileId, id, firstName, lastName, longId)
|
||||
teacherList.put(id, teacher)
|
||||
teacher
|
||||
}
|
||||
}
|
||||
|
||||
fun getTeacherByFirstLast(nameFirstLast: String, longId: String? = null): Teacher {
|
||||
val nameParts = nameFirstLast.split(" ")
|
||||
return getTeacher(nameParts[0], nameParts[1], longId)
|
||||
}
|
||||
|
||||
fun getTeacherByLastFirst(nameLastFirst: String, longId: String? = null): Teacher {
|
||||
val nameParts = nameLastFirst.split(" ")
|
||||
return getTeacher(nameParts[1], nameParts[0], longId)
|
||||
}
|
||||
|
||||
fun getEventType(longId: String, name: String): EventType {
|
||||
val id = longId.crc16().toLong()
|
||||
return eventTypes.singleOrNull { it.id == id } ?: run {
|
||||
|
@ -40,7 +40,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
|
||||
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
|
||||
?: return@forEach
|
||||
val subjectName = subjectElement.text().trim()
|
||||
val subject = data.getSubject(subjectId, subjectName)
|
||||
val subject = data.getSubject(subjectId.crc32(), subjectName)
|
||||
|
||||
val dateString = examElement.child(2).text().trim()
|
||||
if (dateString.isBlank()) return@forEach
|
||||
|
@ -53,7 +53,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
|
||||
val subjectId = subjectElement.id().trim()
|
||||
val subjectName = subjectElement.child(0).text().trim()
|
||||
val subject = data.getSubject(subjectId, subjectName)
|
||||
val subject = data.getSubject(subjectId.crc32(), subjectName)
|
||||
|
||||
val gradeType = when {
|
||||
subjectElement.select("#sum").text().isNotBlank() -> TYPE_POINT_SUM
|
||||
|
@ -41,7 +41,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
|
||||
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
|
||||
?: return@forEach
|
||||
val subjectName = subjectElement.text()
|
||||
val subject = data.getSubject(subjectId, subjectName)
|
||||
val subject = data.getSubject(subjectId.crc32(), subjectName)
|
||||
|
||||
val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
|
||||
val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime
|
||||
|
@ -73,7 +73,7 @@ class EdudziennikWebStart(override val data: DataEdudziennik,
|
||||
EDUDZIENNIK_SUBJECTS_START.findAll(text).forEach {
|
||||
val id = it[1].trim()
|
||||
val name = it[2].trim()
|
||||
data.getSubject(id, name)
|
||||
data.getSubject(id.crc32(), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
|
||||
|
||||
import org.jsoup.Jsoup
|
||||
import pl.szczodrzynski.edziennik.crc32
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SUBJECT_ID
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_TEACHER_ID
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
@ -89,7 +90,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
|
||||
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
|
||||
?: return@forEachIndexed
|
||||
val subjectName = subjectElement.text().trim()
|
||||
val subject = data.getSubject(subjectId, subjectName)
|
||||
val subject = data.getSubject(subjectId.crc32(), subjectName)
|
||||
|
||||
/* Getting teacher */
|
||||
|
||||
|
@ -18,6 +18,7 @@ const val ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE = 2050
|
||||
const val ENDPOINT_MOBIDZIENNIK_WEB_MANUALS = 2100
|
||||
const val ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL = 2200
|
||||
const val ENDPOINT_MOBIDZIENNIK_WEB_HOMEWORK = 2300 // not used as an endpoint
|
||||
const val ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE = 2400
|
||||
const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000
|
||||
|
||||
val MobidziennikFeatures = listOf(
|
||||
@ -38,6 +39,12 @@ val MobidziennikFeatures = listOf(
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Timetable - web scraping - does nothing if the API_MAIN timetable is enough.
|
||||
*/
|
||||
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TIMETABLE, listOf(
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE to LOGIN_METHOD_MOBIDZIENNIK_WEB
|
||||
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)),
|
||||
/**
|
||||
* Agenda - "API" + web scraping.
|
||||
*/
|
||||
|
@ -84,6 +84,10 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
|
||||
MobidziennikWebManuals(data, lastSync, onSuccess)
|
||||
}*/
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE-> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
MobidziennikWebTimetable(data, lastSync, onSuccess)
|
||||
}
|
||||
else -> onSuccess(endpointId)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-9-8.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import org.jsoup.Jsoup
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
import kotlin.collections.set
|
||||
import kotlin.text.replace
|
||||
|
||||
class MobidziennikWebTimetable(
|
||||
override val data: DataMobidziennik,
|
||||
override val lastSync: Long?,
|
||||
val onSuccess: (endpointId: Int) -> Unit
|
||||
) : MobidziennikWeb(data, lastSync) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebTimetable"
|
||||
}
|
||||
|
||||
private val rangesH = mutableMapOf<ClosedFloatingPointRange<Float>, Date>()
|
||||
private val hoursV = mutableMapOf<Int, Pair<Time, Int?>>()
|
||||
private var startDate: Date
|
||||
|
||||
private fun parseCss(css: String): Map<String, String> {
|
||||
return css.split(";").mapNotNull {
|
||||
val spl = it.split(":")
|
||||
if (spl.size != 2)
|
||||
return@mapNotNull null
|
||||
return@mapNotNull spl[0].trim() to spl[1].trim()
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
private fun getRangeH(h: Float): Date? {
|
||||
return rangesH.entries.firstOrNull {
|
||||
h in it.key
|
||||
}?.value
|
||||
}
|
||||
|
||||
private fun stringToDate(date: String): Date? {
|
||||
val items = date.split(" ")
|
||||
val day = items.getOrNull(0)?.toIntOrNull() ?: return null
|
||||
val year = items.getOrNull(2)?.toIntOrNull() ?: return null
|
||||
val month = when (items.getOrNull(1)) {
|
||||
"stycznia" -> 1
|
||||
"lutego" -> 2
|
||||
"marca" -> 3
|
||||
"kwietnia" -> 4
|
||||
"maja" -> 5
|
||||
"czerwca" -> 6
|
||||
"lipca" -> 7
|
||||
"sierpnia" -> 8
|
||||
"września" -> 9
|
||||
"października" -> 10
|
||||
"listopada" -> 11
|
||||
"grudnia" -> 12
|
||||
else -> return null
|
||||
}
|
||||
return Date(year, month, day)
|
||||
}
|
||||
|
||||
init {
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
val nextWeekEnd = Week.getWeekEnd().stepForward(0, 0, 7)
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
startDate = data.arguments?.getString("weekStart")?.let {
|
||||
Date.fromY_m_d(it)
|
||||
} ?: currentWeekStart
|
||||
|
||||
val syncFutureDate = startDate > nextWeekEnd
|
||||
// TODO: 2021-09-09 make DataRemoveModel keep extra lessons
|
||||
val syncExtraLessons = false && System.currentTimeMillis() - (lastSync ?: 0) > 2 * DAY * MS
|
||||
if (!syncFutureDate && !syncExtraLessons) {
|
||||
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE)
|
||||
}
|
||||
else {
|
||||
val types = when {
|
||||
syncFutureDate -> mutableListOf("podstawowy")//, "pozalekcyjny")
|
||||
syncExtraLessons -> mutableListOf("pozalekcyjny")
|
||||
else -> mutableListOf()
|
||||
}
|
||||
|
||||
syncTypes(types, startDate) {
|
||||
// set as synced now only when not syncing future date
|
||||
// (to avoid waiting 2 days for normal sync after future sync)
|
||||
if (syncExtraLessons)
|
||||
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_TIMETABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncTypes(types: MutableList<String>, startDate: Date, onSuccess: () -> Unit) {
|
||||
if (types.isEmpty()) {
|
||||
onSuccess()
|
||||
return
|
||||
}
|
||||
val type = types.removeAt(0)
|
||||
webGet(TAG, "/dziennik/planlekcji?typ=$type&tydzien=${startDate.stringY_m_d}") { html ->
|
||||
MobidziennikLuckyNumberExtractor(data, html)
|
||||
readRangesH(html)
|
||||
readRangesV(html)
|
||||
readLessons(html)
|
||||
syncTypes(types, startDate, onSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readRangesH(html: String) {
|
||||
val htmlH = Regexes.MOBIDZIENNIK_TIMETABLE_TOP.find(html) ?: return
|
||||
val docH = Jsoup.parse(htmlH.value)
|
||||
|
||||
var posH = 0f
|
||||
for (el in docH.select("div > div")) {
|
||||
val css = parseCss(el.attr("style"))
|
||||
val width = css["width"]
|
||||
?.trimEnd('%')
|
||||
?.toFloatOrNull()
|
||||
?: continue
|
||||
val value = stringToDate(el.attr("title"))
|
||||
?: continue
|
||||
|
||||
val range = posH.rangeTo(posH + width)
|
||||
posH += width
|
||||
|
||||
rangesH[range] = value
|
||||
}
|
||||
}
|
||||
|
||||
private fun readRangesV(html: String) {
|
||||
val htmlV = Regexes.MOBIDZIENNIK_TIMETABLE_LEFT.find(html) ?: return
|
||||
val docV = Jsoup.parse(htmlV.value)
|
||||
|
||||
for (el in docV.select("div > div")) {
|
||||
val css = parseCss(el.attr("style"))
|
||||
val top = css["top"]
|
||||
?.trimEnd('%')
|
||||
?.toFloatOrNull()
|
||||
?: continue
|
||||
val values = el.text().split(" ")
|
||||
|
||||
val time = values.getOrNull(0)?.let {
|
||||
Time.fromH_m(it)
|
||||
} ?: continue
|
||||
val num = values.getOrNull(1)?.toIntOrNull()
|
||||
|
||||
hoursV[(top * 100).toInt()] = time to num
|
||||
}
|
||||
}
|
||||
|
||||
private val whitespaceRegex = "\\s+".toRegex()
|
||||
private val classroomRegex = "\\((.*)\\)".toRegex()
|
||||
private fun cleanup(str: String): List<String> {
|
||||
return str
|
||||
.replace(whitespaceRegex, " ")
|
||||
.replace("\n", "")
|
||||
.replace("<small>", "$")
|
||||
.replace("</small>", "$")
|
||||
.replace("<br />", "\n")
|
||||
.replace("<br/>", "\n")
|
||||
.replace("<br>", "\n")
|
||||
.replace("<br />", "\n")
|
||||
.replace("<br/>", "\n")
|
||||
.replace("<br>", "\n")
|
||||
.replace("<b>", "%")
|
||||
.replace("</b>", "%")
|
||||
.replace("<span>", "")
|
||||
.replace("</span>", "")
|
||||
.split("\n")
|
||||
.map { it.trim() }
|
||||
}
|
||||
|
||||
@SuppressLint("LongLogTag", "LogNotTimber")
|
||||
private fun readLessons(html: String) {
|
||||
val matches = Regexes.MOBIDZIENNIK_TIMETABLE_CELL.findAll(html)
|
||||
|
||||
val noLessonDays = mutableListOf<Date>()
|
||||
for (i in 0..6) {
|
||||
noLessonDays.add(startDate.clone().stepForward(0, 0, i))
|
||||
}
|
||||
|
||||
for (match in matches) {
|
||||
val css = parseCss("${match[1]};${match[2]}")
|
||||
val left = css["left"]?.trimEnd('%')?.toFloatOrNull() ?: continue
|
||||
val top = css["top"]?.trimEnd('%')?.toFloatOrNull() ?: continue
|
||||
val width = css["width"]?.trimEnd('%')?.toFloatOrNull() ?: continue
|
||||
val height = css["height"]?.trimEnd('%')?.toFloatOrNull() ?: continue
|
||||
|
||||
val posH = left + width / 2f
|
||||
val topInt = (top * 100).toInt()
|
||||
val bottomInt = ((top + height) * 100).toInt()
|
||||
|
||||
val lessonDate = getRangeH(posH) ?: continue
|
||||
val (startTime, lessonNumber) = hoursV[topInt] ?: continue
|
||||
val endTime = hoursV[bottomInt]?.first ?: continue
|
||||
|
||||
noLessonDays.remove(lessonDate)
|
||||
|
||||
var typeName: String? = null
|
||||
var subjectName: String? = null
|
||||
var teacherName: String? = null
|
||||
var classroomName: String? = null
|
||||
var teamName: String? = null
|
||||
val items = (cleanup(match[3]) + cleanup(match[4])).toMutableList()
|
||||
|
||||
var length = 0
|
||||
while (items.isNotEmpty() && length != items.size) {
|
||||
length = items.size
|
||||
val toRemove = mutableListOf<String?>()
|
||||
items.forEachIndexed { i, item ->
|
||||
when {
|
||||
item.isEmpty() ->
|
||||
toRemove.add(item)
|
||||
item.contains(":") && item.contains(" - ") ->
|
||||
toRemove.add(item)
|
||||
|
||||
item.startsWith("%") -> {
|
||||
subjectName = item.trim('%')
|
||||
toRemove.add(item)
|
||||
toRemove.add(items[0])
|
||||
}
|
||||
|
||||
item.startsWith("&") -> {
|
||||
typeName = item.trim('&')
|
||||
toRemove.add(item)
|
||||
}
|
||||
typeName != null && (item.contains(typeName!!) || item.contains("</small>")) -> {
|
||||
toRemove.add(item)
|
||||
}
|
||||
|
||||
item.contains("(") && item.contains(")") -> {
|
||||
classroomName = classroomRegex.find(item)?.get(1)
|
||||
items[i] = item.replace("($classroomName)", "").trim()
|
||||
}
|
||||
classroomName != null && item.contains(classroomName!!) -> {
|
||||
items[i] = item.replace("($classroomName)", "").trim()
|
||||
}
|
||||
|
||||
item.contains("class=\"wyjatek tooltip\"") ->
|
||||
toRemove.add(item)
|
||||
}
|
||||
}
|
||||
items.removeAll(toRemove)
|
||||
}
|
||||
|
||||
if (items.size == 2 && items[0].contains(" - ")) {
|
||||
val parts = items[0].split(" - ")
|
||||
teamName = parts[0]
|
||||
teacherName = parts[1]
|
||||
}
|
||||
else if (items.size == 2 && typeName?.contains("odwołana") == true) {
|
||||
teamName = items[0]
|
||||
}
|
||||
else if (items.size == 4) {
|
||||
teamName = items[0]
|
||||
teacherName = items[1]
|
||||
}
|
||||
|
||||
val type = when (typeName) {
|
||||
"zastępstwo" -> Lesson.TYPE_CHANGE
|
||||
"lekcja odwołana", "odwołana" -> Lesson.TYPE_CANCELLED
|
||||
else -> Lesson.TYPE_NORMAL
|
||||
}
|
||||
val subject = subjectName?.let { data.getSubject(null, it) }
|
||||
val teacher = teacherName?.let { data.getTeacherByLastFirst(it) }
|
||||
val team = teamName?.let { data.getTeam(
|
||||
id = null,
|
||||
name = it,
|
||||
schoolCode = data.loginServerName ?: return@let null,
|
||||
isTeamClass = false
|
||||
) }
|
||||
|
||||
Lesson(data.profileId, -1).also {
|
||||
it.type = type
|
||||
if (type == Lesson.TYPE_CANCELLED) {
|
||||
it.oldDate = lessonDate
|
||||
it.oldLessonNumber = lessonNumber
|
||||
it.oldStartTime = startTime
|
||||
it.oldEndTime = endTime
|
||||
it.oldSubjectId = subject?.id ?: -1
|
||||
it.oldTeamId = team?.id ?: -1
|
||||
}
|
||||
else {
|
||||
it.date = lessonDate
|
||||
it.lessonNumber = lessonNumber
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subject?.id ?: -1
|
||||
it.teacherId = teacher?.id ?: -1
|
||||
it.teamId = team?.id ?: -1
|
||||
it.classroom = classroomName
|
||||
}
|
||||
|
||||
it.id = it.buildId()
|
||||
|
||||
val seen = profile?.empty == false || lessonDate < Date.getToday()
|
||||
|
||||
if (it.type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
it.id,
|
||||
seen,
|
||||
seen
|
||||
)
|
||||
)
|
||||
}
|
||||
data.lessonList += it
|
||||
}
|
||||
}
|
||||
|
||||
for (date in noLessonDays) {
|
||||
data.lessonList += Lesson(data.profileId, date.value.toLong()).also {
|
||||
it.type = Lesson.TYPE_NO_LESSONS
|
||||
it.date = date
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,39 +81,4 @@ class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
||||
|
||||
val loginShort: String?
|
||||
get() = studentLogin?.split('@')?.get(0)
|
||||
|
||||
fun getSubject(name: String): Subject {
|
||||
val id = name.crc32()
|
||||
return subjectList.singleOrNull { it.id == id } ?: run {
|
||||
val subject = Subject(profileId, id, name, name)
|
||||
subjectList.put(id, subject)
|
||||
subject
|
||||
}
|
||||
}
|
||||
|
||||
fun getTeacher(firstName: String, lastName: String): Teacher {
|
||||
val name = "$firstName $lastName".fixName()
|
||||
return teacherList.singleOrNull { it.fullName == name } ?: run {
|
||||
val id = name.crc32()
|
||||
val teacher = Teacher(profileId, id, firstName, lastName)
|
||||
teacherList.put(id, teacher)
|
||||
teacher
|
||||
}
|
||||
}
|
||||
|
||||
fun getTeam(name: String? = null): Team {
|
||||
if (name == "cała klasa" || name == null) return teamClass ?: run {
|
||||
val id = className!!.crc32()
|
||||
val teamCode = "$schoolShortName:$className"
|
||||
val team = Team(profileId, id, className, Team.TYPE_CLASS, teamCode, -1)
|
||||
teamList.put(id, team)
|
||||
return team
|
||||
} else {
|
||||
val id = name.crc32()
|
||||
val teamCode = "$schoolShortName:$name"
|
||||
val team = Team(profileId, id, name, Team.TYPE_VIRTUAL, teamCode, -1)
|
||||
teamList.put(id, team)
|
||||
return team
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class PodlasieApiFinalGrades(val data: DataPodlasie, val rows: List<JsonObject>)
|
||||
}
|
||||
|
||||
val subjectName = grade.getString("SchoolSubject") ?: return@forEach
|
||||
val subject = data.getSubject(subjectName)
|
||||
val subject = data.getSubject(null, subjectName)
|
||||
|
||||
val addedDate = if (profile.empty) profile.getSemesterStart(semester).inMillis
|
||||
else System.currentTimeMillis()
|
||||
|
@ -34,7 +34,7 @@ class PodlasieApiGrades(val data: DataPodlasie, val rows: List<JsonObject>) {
|
||||
val teacher = data.getTeacher(teacherFirstName, teacherLastName)
|
||||
|
||||
val subjectName = grade.getString("SchoolSubject") ?: return@forEach
|
||||
val subject = data.getSubject(subjectName)
|
||||
val subject = data.getSubject(null, subjectName)
|
||||
|
||||
val addedDate = grade.getString("ReceivedDate")?.let { Date.fromY_m_d(it).inMillis }
|
||||
?: System.currentTimeMillis()
|
||||
|
@ -22,7 +22,13 @@ class PodlasieApiMain(override val data: DataPodlasie,
|
||||
|
||||
init {
|
||||
apiGet(TAG, PODLASIE_API_USER_ENDPOINT) { json ->
|
||||
data.getTeam() // Save the class team when it doesn't exist.
|
||||
// Save the class team when it doesn't exist.
|
||||
data.getTeam(
|
||||
id = null,
|
||||
name = data.className ?: "",
|
||||
schoolCode = data.schoolShortName ?: "",
|
||||
isTeamClass = true
|
||||
)
|
||||
|
||||
json.getInt("LuckyNumber")?.let { PodlasieApiLuckyNumber(data, it) }
|
||||
json.getJsonArray("Teacher")?.asJsonObjectList()?.let { PodlasieApiTeachers(data, it) }
|
||||
|
@ -43,14 +43,21 @@ class PodlasieApiTimetable(val data: DataPodlasie, rows: List<JsonObject>) {
|
||||
val startTime = lesson.getString("TimeFrom")?.let { Time.fromH_m_s(it) }
|
||||
?: return@forEach
|
||||
val endTime = lesson.getString("TimeTo")?.let { Time.fromH_m_s(it) } ?: return@forEach
|
||||
val subject = lesson.getString("SchoolSubject")?.let { data.getSubject(it) }
|
||||
val subject = lesson.getString("SchoolSubject")?.let { data.getSubject(null, it) }
|
||||
?: return@forEach
|
||||
|
||||
val teacherFirstName = lesson.getString("TeacherFirstName") ?: return@forEach
|
||||
val teacherLastName = lesson.getString("TeacherLastName") ?: return@forEach
|
||||
val teacher = data.getTeacher(teacherFirstName, teacherLastName)
|
||||
|
||||
val team = lesson.getString("Group")?.let { data.getTeam(it) } ?: return@forEach
|
||||
val team = lesson.getString("Group")?.let {
|
||||
data.getTeam(
|
||||
id = null,
|
||||
name = it,
|
||||
schoolCode = data.schoolShortName ?: "",
|
||||
isTeamClass = it == "cała klasa"
|
||||
)
|
||||
} ?: return@forEach
|
||||
val classroom = lesson.getString("Room")
|
||||
|
||||
Lesson(data.profileId, -1).also {
|
||||
|
@ -2,6 +2,7 @@ package pl.szczodrzynski.edziennik.data.api.models
|
||||
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import androidx.core.util.set
|
||||
import androidx.core.util.size
|
||||
import androidx.room.OnConflictStrategy
|
||||
import com.google.gson.JsonObject
|
||||
@ -376,4 +377,108 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
|
||||
fun startProgress(stringRes: Int) {
|
||||
callback.onStartProgress(stringRes)
|
||||
}
|
||||
|
||||
/* _ _ _ _ _
|
||||
| | | | | (_) |
|
||||
| | | | |_ _| |___
|
||||
| | | | __| | / __|
|
||||
| |__| | |_| | \__ \
|
||||
\____/ \__|_|_|__*/
|
||||
fun getSubject(id: Long?, name: String, shortName: String = name): Subject {
|
||||
var subject = subjectList.singleOrNull { it.id == id }
|
||||
if (subject == null)
|
||||
subject = subjectList.singleOrNull { it.longName == name }
|
||||
if (subject == null)
|
||||
subject = subjectList.singleOrNull { it.shortName == name }
|
||||
|
||||
if (subject == null) {
|
||||
subject = Subject(
|
||||
profileId,
|
||||
id ?: name.crc32(),
|
||||
name,
|
||||
shortName
|
||||
)
|
||||
subjectList[subject.id] = subject
|
||||
}
|
||||
return subject
|
||||
}
|
||||
|
||||
fun getTeam(id: Long?, name: String, schoolCode: String, isTeamClass: Boolean = false): Team {
|
||||
if (isTeamClass && teamClass != null)
|
||||
return teamClass as Team
|
||||
var team = teamList.singleOrNull { it.id == id }
|
||||
|
||||
val namePlain = name.replace(" ", "")
|
||||
if (team == null)
|
||||
team = teamList.singleOrNull { it.name.replace(" ", "") == namePlain }
|
||||
|
||||
if (team == null) {
|
||||
team = Team(
|
||||
profileId,
|
||||
id ?: name.crc32(),
|
||||
name,
|
||||
if (isTeamClass) Team.TYPE_CLASS else Team.TYPE_VIRTUAL,
|
||||
"$schoolCode:$name",
|
||||
-1
|
||||
)
|
||||
teamList[team.id] = team
|
||||
}
|
||||
return team
|
||||
}
|
||||
|
||||
fun getTeacher(firstName: String, lastName: String, loginId: String? = null): Teacher {
|
||||
val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" }
|
||||
return validateTeacher(teacher, firstName, lastName, loginId)
|
||||
}
|
||||
|
||||
fun getTeacher(firstNameChar: Char, lastName: String, loginId: String? = null): Teacher {
|
||||
val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" }
|
||||
return validateTeacher(teacher, firstNameChar.toString(), lastName, loginId)
|
||||
}
|
||||
|
||||
fun getTeacherByLastFirst(nameLastFirst: String, loginId: String? = null): Teacher {
|
||||
val nameParts = nameLastFirst.split(" ")
|
||||
return if (nameParts.size == 1)
|
||||
getTeacher(nameParts[0], "", loginId)
|
||||
else
|
||||
getTeacher(nameParts[1], nameParts[0], loginId)
|
||||
}
|
||||
|
||||
fun getTeacherByFirstLast(nameFirstLast: String, loginId: String? = null): Teacher {
|
||||
val nameParts = nameFirstLast.split(" ")
|
||||
return if (nameParts.size == 1)
|
||||
getTeacher(nameParts[0], "", loginId)
|
||||
else
|
||||
getTeacher(nameParts[0], nameParts[1], loginId)
|
||||
}
|
||||
|
||||
fun getTeacherByFDotLast(nameFDotLast: String, loginId: String? = null): Teacher {
|
||||
val nameParts = nameFDotLast.split(".")
|
||||
return if (nameParts.size == 1)
|
||||
getTeacher(nameParts[0], "", loginId)
|
||||
else
|
||||
getTeacher(nameParts[0][0], nameParts[1], loginId)
|
||||
}
|
||||
|
||||
fun getTeacherByFDotSpaceLast(nameFDotSpaceLast: String, loginId: String? = null): Teacher {
|
||||
val nameParts = nameFDotSpaceLast.split(".")
|
||||
return if (nameParts.size == 1)
|
||||
getTeacher(nameParts[0], "", loginId)
|
||||
else
|
||||
getTeacher(nameParts[0][0], nameParts[1], loginId)
|
||||
}
|
||||
|
||||
private fun validateTeacher(teacher: Teacher?, firstName: String, lastName: String, loginId: String?): Teacher {
|
||||
val obj = teacher ?: Teacher(profileId, -1, firstName, lastName, loginId).apply {
|
||||
id = fullName.crc32()
|
||||
teacherList[id] = this
|
||||
}
|
||||
return obj.also {
|
||||
if (loginId != null && it.loginId != null)
|
||||
it.loginId = loginId
|
||||
if (firstName.length > 1)
|
||||
it.name = firstName
|
||||
it.surname = lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user