mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-02-08 00:54:37 +01:00
[API/Usos] Implement basic grades support
This commit is contained in:
parent
514fbafd00
commit
d44b85073a
@ -10,10 +10,12 @@ import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
|
||||
const val ENDPOINT_USOS_API_USER = 7000
|
||||
const val ENDPOINT_USOS_API_TERMS = 7010
|
||||
const val ENDPOINT_USOS_API_COURSES = 7020
|
||||
const val ENDPOINT_USOS_API_TIMETABLE = 7030
|
||||
const val ENDPOINT_USOS_API_USER = 7000
|
||||
const val ENDPOINT_USOS_API_TERMS = 7010
|
||||
const val ENDPOINT_USOS_API_COURSES = 7020
|
||||
const val ENDPOINT_USOS_API_TIMETABLE = 7030
|
||||
const val ENDPOINT_USOS_API_ECTS_POINTS = 7040
|
||||
const val ENDPOINT_USOS_API_EXAM_REPORTS = 7050
|
||||
|
||||
val UsosFeatures = listOf(
|
||||
/*
|
||||
@ -39,4 +41,12 @@ val UsosFeatures = listOf(
|
||||
Feature(LoginType.USOS, FeatureType.TIMETABLE, listOf(
|
||||
ENDPOINT_USOS_API_TIMETABLE to LoginMethod.USOS_API,
|
||||
)),
|
||||
|
||||
/*
|
||||
* Grades
|
||||
*/
|
||||
Feature(LoginType.USOS, FeatureType.GRADES, listOf(
|
||||
ENDPOINT_USOS_API_ECTS_POINTS to LoginMethod.USOS_API,
|
||||
ENDPOINT_USOS_API_EXAM_REPORTS to LoginMethod.USOS_API,
|
||||
)),
|
||||
)
|
||||
|
@ -8,6 +8,8 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.data.web.TemplateWebSample
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiCourses
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiEctsPoints
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiExamReports
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiTerms
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiTimetable
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiUser
|
||||
@ -58,6 +60,14 @@ class UsosData(val data: DataUsos, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
UsosApiTimetable(data, lastSync, onSuccess)
|
||||
}
|
||||
ENDPOINT_USOS_API_ECTS_POINTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
|
||||
UsosApiEctsPoints(data, lastSync, onSuccess)
|
||||
}
|
||||
ENDPOINT_USOS_API_EXAM_REPORTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
UsosApiExamReports(data, lastSync, onSuccess)
|
||||
}
|
||||
else -> onSuccess(endpointId)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2025-1-31.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_USOS_API_INCOMPLETE_RESPONSE
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.ENDPOINT_USOS_API_ECTS_POINTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.UsosApi
|
||||
import pl.szczodrzynski.edziennik.ext.DAY
|
||||
import pl.szczodrzynski.edziennik.ext.filter
|
||||
|
||||
class UsosApiEctsPoints(
|
||||
override val data: DataUsos,
|
||||
override val lastSync: Long?,
|
||||
val onSuccess: (endpointId: Int) -> Unit,
|
||||
) : UsosApi(data, lastSync) {
|
||||
companion object {
|
||||
const val TAG = "UsosApiEctsPoints"
|
||||
}
|
||||
|
||||
init {
|
||||
apiRequest<JsonObject>(
|
||||
tag = TAG,
|
||||
service = "courses/user_ects_points",
|
||||
responseType = ResponseType.OBJECT,
|
||||
) { json, response ->
|
||||
if (!processResponse(json)) {
|
||||
data.error(TAG, ERROR_USOS_API_INCOMPLETE_RESPONSE, response)
|
||||
return@apiRequest
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_USOS_API_ECTS_POINTS, 2 * DAY)
|
||||
onSuccess(ENDPOINT_USOS_API_ECTS_POINTS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processResponse(json: JsonObject): Boolean {
|
||||
for ((_, coursePointsEl) in json.entrySet()) {
|
||||
if (!coursePointsEl.isJsonObject)
|
||||
continue
|
||||
for ((courseId, pointsEl) in coursePointsEl.asJsonObject.entrySet()) {
|
||||
if (!pointsEl.isJsonPrimitive)
|
||||
continue
|
||||
val gradeCategories = data.gradeCategories
|
||||
.filter { it.text == courseId }
|
||||
gradeCategories.forEach {
|
||||
it.weight = pointsEl.asString.toFloatOrNull() ?: -1.0f
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2025-1-31.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_USOS_API_INCOMPLETE_RESPONSE
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.ENDPOINT_USOS_API_EXAM_REPORTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.UsosApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.ext.getBoolean
|
||||
import pl.szczodrzynski.edziennik.ext.getInt
|
||||
import pl.szczodrzynski.edziennik.ext.getJsonArray
|
||||
import pl.szczodrzynski.edziennik.ext.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.ext.getLong
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
import pl.szczodrzynski.edziennik.ext.join
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class UsosApiExamReports(
|
||||
override val data: DataUsos,
|
||||
override val lastSync: Long?,
|
||||
val onSuccess: (endpointId: Int) -> Unit,
|
||||
) : UsosApi(data, lastSync) {
|
||||
companion object {
|
||||
const val TAG = "UsosApiExamReports"
|
||||
}
|
||||
|
||||
init {
|
||||
apiRequest<JsonObject>(
|
||||
tag = TAG,
|
||||
service = "examrep/user2",
|
||||
fields = listOf(
|
||||
"type_description",
|
||||
"course_unit" to listOf("id", "course_name"),
|
||||
"sessions" to listOf(
|
||||
"description",
|
||||
"issuer_grades" to listOf(
|
||||
"exam_id",
|
||||
"exam_session_number",
|
||||
"value_symbol",
|
||||
// "value_description",
|
||||
"passes",
|
||||
"counts_into_average",
|
||||
"date_modified",
|
||||
"modification_author",
|
||||
"comment",
|
||||
),
|
||||
),
|
||||
),
|
||||
responseType = ResponseType.OBJECT,
|
||||
) { json, response ->
|
||||
if (!processResponse(json)) {
|
||||
data.error(TAG, ERROR_USOS_API_INCOMPLETE_RESPONSE, response)
|
||||
return@apiRequest
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Grades.all())
|
||||
data.setSyncNext(ENDPOINT_USOS_API_EXAM_REPORTS, SYNC_ALWAYS)
|
||||
onSuccess(ENDPOINT_USOS_API_EXAM_REPORTS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processResponse(json: JsonObject): Boolean {
|
||||
for ((_, courseEditionEl) in json.entrySet()) {
|
||||
if (!courseEditionEl.isJsonObject)
|
||||
continue
|
||||
for ((courseId, examReportsEl) in courseEditionEl.asJsonObject.entrySet()) {
|
||||
if (!examReportsEl.isJsonArray)
|
||||
continue
|
||||
for (examReportEl in examReportsEl.asJsonArray) {
|
||||
if (!examReportEl.isJsonObject)
|
||||
continue
|
||||
val examReport = examReportEl.asJsonObject
|
||||
processExamReport(courseId, examReport)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processExamReport(courseId: String, examReport: JsonObject) {
|
||||
val typeDescription = examReport.getLangString("type_description")
|
||||
val courseUnit = examReport.getJsonObject("course_unit")
|
||||
?: return
|
||||
val courseUnitId = courseUnit.getString("id")?.toLongOrNull()
|
||||
?: return
|
||||
val courseName = courseUnit.getLangString("course_name")
|
||||
?: return
|
||||
val sessions = examReport.getJsonArray("sessions")
|
||||
?: return
|
||||
|
||||
for (sessionEl in sessions) {
|
||||
if (!sessionEl.isJsonObject)
|
||||
continue
|
||||
val session = sessionEl.asJsonObject
|
||||
|
||||
val sessionDescription = session.getLangString("description")
|
||||
val issuerGrade = session.getJsonObject("issuer_grades") ?: continue
|
||||
|
||||
val examId = issuerGrade.getInt("exam_id") ?: continue
|
||||
val sessionNumber = issuerGrade.getInt("exam_session_number") ?: continue
|
||||
val valueSymbol = issuerGrade.getString("value_symbol") ?: continue
|
||||
val passes = issuerGrade.getBoolean("passes")
|
||||
val countsIntoAverage = issuerGrade.getString("counts_into_average") ?: "T"
|
||||
val dateModified = issuerGrade.getString("date_modified")
|
||||
val modificationAuthorId = issuerGrade.getJsonObject("modification_author")
|
||||
?.getLong("id") ?: -1L
|
||||
val comment = issuerGrade.getString("comment")
|
||||
|
||||
val gradeCategory = data.gradeCategories[courseUnitId]
|
||||
val classType = gradeCategory?.columns?.get(0)
|
||||
val value = valueSymbol.toFloatOrNull() ?: 0.0f
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId = profileId,
|
||||
id = examId * 10L + sessionNumber,
|
||||
name = valueSymbol,
|
||||
type = TYPE_NORMAL,
|
||||
value = value,
|
||||
weight = if (countsIntoAverage == "T") gradeCategory?.weight ?: 0.0f else 0.0f,
|
||||
color = (if (passes == true) 0xFF465FB3 else 0xFFB71C1C).toInt(),
|
||||
category = typeDescription,
|
||||
description = listOfNotNull(classType, sessionDescription).join(" - "),
|
||||
comment = comment,
|
||||
semester = 1,
|
||||
teacherId = modificationAuthorId,
|
||||
subjectId = data.getSubject(
|
||||
id = null,
|
||||
name = courseName,
|
||||
shortName = courseId,
|
||||
).id,
|
||||
addedDate = Date.fromIso(dateModified),
|
||||
)
|
||||
|
||||
if (sessionNumber > 1) {
|
||||
val origId = examId * 10L + sessionNumber - 1
|
||||
val grades = data.gradeList.filter { it.id == origId }
|
||||
grades.firstOrNull()?.parentId = gradeObject.id
|
||||
gradeObject.isImprovement = true
|
||||
}
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
profileId,
|
||||
MetadataType.GRADE,
|
||||
gradeObject.id,
|
||||
profile?.empty ?: false,
|
||||
profile?.empty ?: false,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -93,6 +93,7 @@ internal val FEATURES_PODLASIE = setOf(
|
||||
internal val FEATURES_USOS = setOf(
|
||||
TIMETABLE,
|
||||
AGENDA,
|
||||
GRADES,
|
||||
|
||||
STUDENT_INFO,
|
||||
STUDENT_NUMBER,
|
||||
|
@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_AVG
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.SchoolType
|
||||
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.ext.asColoredSpannable
|
||||
import pl.szczodrzynski.edziennik.ext.get
|
||||
@ -158,6 +159,19 @@ class GradesManager(val app: App) : CoroutineScope {
|
||||
}
|
||||
}
|
||||
type == TYPE_NORMAL && defColor -> grade.color and 0xffffff
|
||||
type == TYPE_NORMAL && app.profile.loginStoreType.schoolType == SchoolType.UNIVERSITY -> {
|
||||
when (grade.name.lowercase()) {
|
||||
"zal" -> 0x4caf50
|
||||
"nb", "nk" -> 0xff7043
|
||||
"2.0", "nzal" -> 0xff3d00
|
||||
"3.0" -> 0xffff00
|
||||
"3.5" -> 0xc6ff00
|
||||
"4.0" -> 0x76ff03
|
||||
"4.5" -> 0x64dd17
|
||||
"5.0" -> 0x00c853
|
||||
else -> grade.color and 0xffffff
|
||||
}
|
||||
}
|
||||
type in TYPE_NORMAL..TYPE_YEAR_FINAL -> {
|
||||
when (grade.name.lowercase()) {
|
||||
"+", "++", "+++" -> 0x4caf50
|
||||
|
Loading…
x
Reference in New Issue
Block a user