From e04b519e9b278e1ba12be6ea7e3de38aa3b5775f Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Tue, 24 Dec 2019 13:41:06 +0100 Subject: [PATCH] [API/Edudziennik] Move getting grades and info to new endpoints. --- .../edziennik/data/api/Errors.kt | 2 + .../edudziennik/data/EdudziennikWeb.kt | 10 +- .../data/web/EdudziennikWebStart.kt | 214 +----------------- .../data/web/EdudziennikWebStartGrades.kt | 185 +++++++++++++++ .../data/web/EdudziennikWebStartInfo.kt | 51 +++++ 5 files changed, 249 insertions(+), 213 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartGrades.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartInfo.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt index c57a9f17..82a05b81 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt @@ -162,6 +162,7 @@ const val ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN = 501 const val ERROR_LOGIN_EDUDZIENNIK_WEB_OTHER = 510 const val ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID = 511 const val ERROR_EDUDZIENNIK_WEB_TIMETABLE_NOT_PUBLIC = 520 +const val ERROR_EDUDZIENNIK_WEB_TEAM_MISSING = 530 const val ERROR_TEMPLATE_WEB_OTHER = 801 @@ -180,5 +181,6 @@ const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911 const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912 const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913 const val EXCEPTION_IDZIENNIK_API_REQUEST = 914 +const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920 const val LOGIN_NO_ARGUMENTS = 1201 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/EdudziennikWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/EdudziennikWeb.kt index c5c80bd5..f59f8128 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/EdudziennikWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/EdudziennikWeb.kt @@ -11,6 +11,7 @@ import okhttp3.Cookie import pl.szczodrzynski.edziennik.data.api.EDUDZIENNIK_USER_AGENT import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE import pl.szczodrzynski.edziennik.data.api.ERROR_RESPONSE_EMPTY +import pl.szczodrzynski.edziennik.data.api.EXCEPTION_EDUDZIENNIK_WEB_REQUEST import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.utils.Utils.d @@ -42,7 +43,14 @@ open class EdudziennikWeb(open val data: DataEdudziennik) { return } - onSuccess(text) + try { + onSuccess(text) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_EDUDZIENNIK_WEB_REQUEST) + .withThrowable(e) + .withResponse(response) + .withApiResponse(text)) + } } override fun onFailure(response: Response?, throwable: Throwable?) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStart.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStart.kt index 47ce071b..bcaba702 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStart.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStart.kt @@ -4,30 +4,11 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web -import android.graphics.Color import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import pl.szczodrzynski.edziennik.colorFromCssName -import pl.szczodrzynski.edziennik.crc32 -import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_CLASS_DETAIL_ID -import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_CLASS_DETAIL_NAME -import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_GRADE_ID -import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SCHOOL_DETAIL_ID -import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SCHOOL_DETAIL_NAME -import pl.szczodrzynski.edziennik.data.api.Regexes.STYLE_CSS_COLOR import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_START import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb -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.grades.Grade -import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.* -import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata -import pl.szczodrzynski.edziennik.data.db.modules.teams.Team -import pl.szczodrzynski.edziennik.firstLettersName -import pl.szczodrzynski.edziennik.get -import pl.szczodrzynski.edziennik.utils.Utils -import pl.szczodrzynski.edziennik.utils.models.Date class EdudziennikWebStart(override val data: DataEdudziennik, val onSuccess: () -> Unit) : EdudziennikWeb(data) { @@ -39,202 +20,11 @@ class EdudziennikWebStart(override val data: DataEdudziennik, webGet(TAG, data.studentEndpoint + "start") { text -> val doc = Jsoup.parse(text) - getInfo(text) - getGrades(doc) + EdudziennikWebStartInfo(data, text) + EdudziennikWebStartGrades(data, doc) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_START, SYNC_ALWAYS) onSuccess() } } - - private fun getInfo(text: String) { - val schoolId = EDUDZIENNIK_SCHOOL_DETAIL_ID.find(text)?.get(1)?.trim() - val schoolName = EDUDZIENNIK_SCHOOL_DETAIL_NAME.find(text)?.get(1)?.trim() - data.schoolId = schoolId - - val classId = EDUDZIENNIK_CLASS_DETAIL_ID.find(text)?.get(1)?.trim() - val className = EDUDZIENNIK_CLASS_DETAIL_NAME.find(text)?.get(1)?.trim() - data.classId = classId - - if (classId == null || className == null || schoolId == null || schoolName == null) { - // TODO data.error( - return - } - - val teamId = classId.crc32() - val teamCode = schoolId.crc32().toString() + schoolName.firstLettersName + "_edu:" + className - - val teamObject = Team( - profileId, - teamId, - className, - Team.TYPE_CLASS, - teamCode, - -1 - ) - - data.teamClass = teamObject - data.teamList.put(teamObject.id, teamObject) - } - - private fun getGrades(doc: Document) { data.profile?.also { profile -> - val subjects = doc.select("#student_grades tbody").firstOrNull()?.children() - - if (subjects.isNullOrEmpty()) return - - subjects.forEach { subjectElement -> - if (subjectElement.id().isBlank()) return@forEach - - val subjectId = subjectElement.id().trim() - val subjectName = subjectElement.child(0).text().trim() - val subject = data.getSubject(subjectId, subjectName) - - val grades = subjectElement.select(".grade") - val gradesInfo = subjectElement.select(".grade-tip") - - val gradeValues = subjects.select(".avg-$subjectId .grade-tip > p").first() - .text().split('+').map { - val split = it.split('*') - val weight = split[0].trim().toFloat() - val value = split[1].trim().toFloat() - - Pair(value, weight) - } - - grades.forEachIndexed { index, gradeElement -> - val id = EDUDZIENNIK_GRADE_ID.find(gradeElement.attr("href"))?.get(1)?.crc32() - ?: return@forEachIndexed - val (value, weight) = gradeValues[index] - val name = gradeElement.text().trim().let { - if (it.contains(',') || it.contains('.')) { - val replaced = it.replace(',', '.') - val float = replaced.toFloatOrNull() - - if (float != null && float % 1 == 0f) float.toInt().toString() - else it - } else it - } - - val info = gradesInfo[index] - val category = info.child(4).text().trim() - - val (teacherLastName, teacherFirstName) = info.child(1).text().split(' ') - val teacher = data.getTeacher(teacherFirstName, teacherLastName) - - val addedDate = info.child(2).text().split(' ').let { - val day = it[0].toInt() - val month = Utils.monthFromName(it[1]) - val year = it[2].toInt() - - Date(year, month, day).inMillis - } - - val color = STYLE_CSS_COLOR.find(gradeElement.attr("style"))?.get(1)?.let { - if (it.startsWith('#')) Color.parseColor(it) - else colorFromCssName(it) - } ?: -1 - - val gradeObject = Grade( - profileId, - id, - category, - color, - "", - name, - value, - weight, - profile.currentSemester, - teacher.id, - subject.id - ) - - data.gradeList.add(gradeObject) - data.metadataList.add(Metadata( - profileId, - Metadata.TYPE_GRADE, - id, - profile.empty, - profile.empty, - addedDate - )) - } - - val proposed = subjectElement.select(".proposal").firstOrNull()?.text()?.trim() - - if (proposed != null && proposed.isNotBlank()) { - val proposedGradeObject = Grade( - profileId, - (-1 * subject.id) - 1, - "", - -1, - "", - proposed, - proposed.toFloatOrNull() ?: 0f, - 0f, - profile.currentSemester, - -1, - subject.id - ).apply { - type = when (semester) { - 1 -> TYPE_SEMESTER1_PROPOSED - else -> TYPE_SEMESTER2_PROPOSED - } - } - - data.gradeList.add(proposedGradeObject) - data.metadataList.add(Metadata( - profileId, - Metadata.TYPE_GRADE, - proposedGradeObject.id, - profile.empty, - profile.empty, - System.currentTimeMillis() - )) - } - - val final = subjectElement.select(".final").firstOrNull()?.text()?.trim() - - if (final != null && final.isNotBlank()) { - val finalGradeObject = Grade( - profileId, - (-1 * subject.id) - 2, - "", - -1, - "", - final, - final.toFloatOrNull() ?: 0f, - 0f, - profile.currentSemester, - -1, - subject.id - ).apply { - type = when (semester) { - 1 -> TYPE_SEMESTER1_FINAL - else -> TYPE_SEMESTER2_FINAL - } - } - - data.gradeList.add(finalGradeObject) - data.metadataList.add(Metadata( - profileId, - Metadata.TYPE_GRADE, - finalGradeObject.id, - profile.empty, - profile.empty, - System.currentTimeMillis() - )) - } - } - - data.toRemove.addAll(listOf( - TYPE_NORMAL, - TYPE_SEMESTER1_PROPOSED, - TYPE_SEMESTER2_PROPOSED, - TYPE_SEMESTER1_FINAL, - TYPE_SEMESTER2_FINAL - ).map { - DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it) - }) - - }} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartGrades.kt new file mode 100644 index 00000000..974e2332 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartGrades.kt @@ -0,0 +1,185 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-12-24 + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web + +import android.graphics.Color +import org.jsoup.nodes.Document +import pl.szczodrzynski.edziennik.colorFromCssName +import pl.szczodrzynski.edziennik.crc32 +import pl.szczodrzynski.edziennik.data.api.Regexes +import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik +import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.* +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date + +class EdudziennikWebStartGrades(val data: DataEdudziennik, val doc: Document) { + companion object { + const val TAG = "EdudziennikWebStartGrades" + } + + init {data.profile?.also { profile -> + val subjects = doc.select("#student_grades tbody").firstOrNull()?.children() + + if (subjects.isNullOrEmpty()) return@also + + subjects.forEach { subjectElement -> + if (subjectElement.id().isBlank()) return@forEach + + val subjectId = subjectElement.id().trim() + val subjectName = subjectElement.child(0).text().trim() + val subject = data.getSubject(subjectId, subjectName) + + val grades = subjectElement.select(".grade") + val gradesInfo = subjectElement.select(".grade-tip") + + val gradeValues = subjects.select(".avg-$subjectId .grade-tip > p").first() + .text().split('+').map { + val split = it.split('*') + val weight = split[0].trim().toFloat() + val value = split[1].trim().toFloat() + + Pair(value, weight) + } + + grades.forEachIndexed { index, gradeElement -> + val id = Regexes.EDUDZIENNIK_GRADE_ID.find(gradeElement.attr("href"))?.get(1)?.crc32() + ?: return@forEachIndexed + val (value, weight) = gradeValues[index] + val name = gradeElement.text().trim().let { + if (it.contains(',') || it.contains('.')) { + val replaced = it.replace(',', '.') + val float = replaced.toFloatOrNull() + + if (float != null && float % 1 == 0f) float.toInt().toString() + else it + } else it + } + + val info = gradesInfo[index] + val category = info.child(4).text().trim() + + val (teacherLastName, teacherFirstName) = info.child(1).text().split(' ') + val teacher = data.getTeacher(teacherFirstName, teacherLastName) + + val addedDate = info.child(2).text().split(' ').let { + val day = it[0].toInt() + val month = Utils.monthFromName(it[1]) + val year = it[2].toInt() + + Date(year, month, day).inMillis + } + + val color = Regexes.STYLE_CSS_COLOR.find(gradeElement.attr("style"))?.get(1)?.let { + if (it.startsWith('#')) Color.parseColor(it) + else colorFromCssName(it) + } ?: -1 + + val gradeObject = Grade( + data.profileId, + id, + category, + color, + "", + name, + value, + weight, + profile.currentSemester, + teacher.id, + subject.id + ) + + data.gradeList.add(gradeObject) + data.metadataList.add(Metadata( + data.profileId, + Metadata.TYPE_GRADE, + id, + profile.empty, + profile.empty, + addedDate + )) + } + + val proposed = subjectElement.select(".proposal").firstOrNull()?.text()?.trim() + + if (proposed != null && proposed.isNotBlank()) { + val proposedGradeObject = Grade( + data.profileId, + (-1 * subject.id) - 1, + "", + -1, + "", + proposed, + proposed.toFloatOrNull() ?: 0f, + 0f, + profile.currentSemester, + -1, + subject.id + ).apply { + type = when (semester) { + 1 -> TYPE_SEMESTER1_PROPOSED + else -> TYPE_SEMESTER2_PROPOSED + } + } + + data.gradeList.add(proposedGradeObject) + data.metadataList.add(Metadata( + data.profileId, + Metadata.TYPE_GRADE, + proposedGradeObject.id, + profile.empty, + profile.empty, + System.currentTimeMillis() + )) + } + + val final = subjectElement.select(".final").firstOrNull()?.text()?.trim() + + if (final != null && final.isNotBlank()) { + val finalGradeObject = Grade( + data.profileId, + (-1 * subject.id) - 2, + "", + -1, + "", + final, + final.toFloatOrNull() ?: 0f, + 0f, + profile.currentSemester, + -1, + subject.id + ).apply { + type = when (semester) { + 1 -> TYPE_SEMESTER1_FINAL + else -> TYPE_SEMESTER2_FINAL + } + } + + data.gradeList.add(finalGradeObject) + data.metadataList.add(Metadata( + data.profileId, + Metadata.TYPE_GRADE, + finalGradeObject.id, + profile.empty, + profile.empty, + System.currentTimeMillis() + )) + } + } + + data.toRemove.addAll(listOf( + TYPE_NORMAL, + TYPE_SEMESTER1_PROPOSED, + TYPE_SEMESTER2_PROPOSED, + TYPE_SEMESTER1_FINAL, + TYPE_SEMESTER2_FINAL + ).map { + DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it) + }) + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartInfo.kt new file mode 100644 index 00000000..15fe0f68 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebStartInfo.kt @@ -0,0 +1,51 @@ +/* + * 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.ERROR_EDUDZIENNIK_WEB_TEAM_MISSING +import pl.szczodrzynski.edziennik.data.api.Regexes +import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik +import pl.szczodrzynski.edziennik.data.api.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.teams.Team +import pl.szczodrzynski.edziennik.firstLettersName +import pl.szczodrzynski.edziennik.get + +class EdudziennikWebStartInfo(val data: DataEdudziennik, val text: String) { + companion object { + const val TAG = "EdudziennikWebStartInfo" + } + + init { run { + val schoolId = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_ID.find(text)?.get(1)?.trim() + val schoolName = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_NAME.find(text)?.get(1)?.trim() + data.schoolId = schoolId + + val classId = Regexes.EDUDZIENNIK_CLASS_DETAIL_ID.find(text)?.get(1)?.trim() + val className = Regexes.EDUDZIENNIK_CLASS_DETAIL_NAME.find(text)?.get(1)?.trim() + data.classId = classId + + if (classId == null || className == null || schoolId == null || schoolName == null) { + data.error(ApiError(TAG, ERROR_EDUDZIENNIK_WEB_TEAM_MISSING) + .withApiResponse(text)) + return@run + } + + val teamId = classId.crc32() + val teamCode = schoolId.crc32().toString() + schoolName.firstLettersName + "_edu:" + className + + val teamObject = Team( + data.profileId, + teamId, + className, + Team.TYPE_CLASS, + teamCode, + -1 + ) + + data.teamClass = teamObject + data.teamList.put(teamObject.id, teamObject) + }} +}