forked from github/szkolny
[API/Usos] Implement Timetable.
This commit is contained in:
parent
8d174bda01
commit
4de066bf5f
@ -5,25 +5,35 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.usos
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.FEATURE_ALWAYS_NEEDED
|
||||
import pl.szczodrzynski.edziennik.data.api.FEATURE_STUDENT_INFO
|
||||
import pl.szczodrzynski.edziennik.data.api.FEATURE_TEAM_INFO
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Feature
|
||||
|
||||
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_USER = 7000
|
||||
const val ENDPOINT_USOS_API_TERMS = 7010
|
||||
const val ENDPOINT_USOS_API_COURSES = 7020
|
||||
const val ENDPOINT_USOS_API_TIMETABLE = 7030
|
||||
|
||||
val UsosFeatures = listOf(
|
||||
/*
|
||||
* Student information
|
||||
*/
|
||||
Feature(LOGIN_TYPE_USOS, FEATURE_STUDENT_INFO, listOf(
|
||||
ENDPOINT_USOS_API_USER to LOGIN_METHOD_USOS_API,
|
||||
), listOf(LOGIN_METHOD_USOS_API)),
|
||||
|
||||
/*
|
||||
* Terms & courses
|
||||
*/
|
||||
Feature(LOGIN_TYPE_USOS, FEATURE_SCHOOL_INFO, listOf(
|
||||
ENDPOINT_USOS_API_TERMS to LOGIN_METHOD_USOS_API,
|
||||
), listOf(LOGIN_METHOD_USOS_API)),
|
||||
|
||||
Feature(LOGIN_TYPE_USOS, FEATURE_TEAM_INFO, listOf(
|
||||
ENDPOINT_USOS_API_COURSES to LOGIN_METHOD_USOS_API,
|
||||
), listOf(LOGIN_METHOD_USOS_API)),
|
||||
|
||||
/*
|
||||
* Timetable
|
||||
*/
|
||||
Feature(LOGIN_TYPE_USOS, FEATURE_TIMETABLE, listOf(
|
||||
ENDPOINT_USOS_API_TIMETABLE to LOGIN_METHOD_USOS_API,
|
||||
), listOf(LOGIN_METHOD_USOS_API)),
|
||||
)
|
||||
|
@ -6,12 +6,10 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.usos.data
|
||||
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.data.web.TemplateWebSample
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.ENDPOINT_USOS_API_COURSES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.ENDPOINT_USOS_API_TERMS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.ENDPOINT_USOS_API_USER
|
||||
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.UsosApiTerms
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api.UsosApiTimetable
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class UsosData(val data: DataUsos, val onSuccess: () -> Unit) {
|
||||
@ -55,6 +53,10 @@ class UsosData(val data: DataUsos, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_teams)
|
||||
UsosApiCourses(data, lastSync, onSuccess)
|
||||
}
|
||||
ENDPOINT_USOS_API_TIMETABLE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
UsosApiTimetable(data, lastSync, onSuccess)
|
||||
}
|
||||
else -> onSuccess(endpointId)
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,12 @@ class UsosApiCourses(
|
||||
// "terms" to listOf("id", "name", "start_date", "end_date"),
|
||||
"course_editions" to listOf(
|
||||
"course_id",
|
||||
// "course_name",
|
||||
"course_name",
|
||||
// "term_id",
|
||||
"user_groups" to listOf(
|
||||
"course_unit_id",
|
||||
"group_number",
|
||||
"class_type",
|
||||
// "class_type",
|
||||
"class_type_id",
|
||||
// "lecturers",
|
||||
),
|
||||
@ -62,18 +62,18 @@ class UsosApiCourses(
|
||||
var hasValidTeam = false
|
||||
for (courseEdition in courseEditions) {
|
||||
val courseId = courseEdition.getString("course_id") ?: continue
|
||||
// val courseName = courseEdition.getLangString("course_name") ?: continue
|
||||
val courseName = courseEdition.getLangString("course_name") ?: continue
|
||||
val userGroups = courseEdition.getJsonArray("user_groups")?.asJsonObjectList() ?: continue
|
||||
for (userGroup in userGroups) {
|
||||
val courseUnitId = userGroup.getLong("course_unit_id") ?: continue
|
||||
val groupNumber = userGroup.getInt("group_number") ?: continue
|
||||
val classType = userGroup.getLangString("class_type") ?: continue
|
||||
// val classType = userGroup.getLangString("class_type") ?: continue
|
||||
val classTypeId = userGroup.getString("class_type_id") ?: continue
|
||||
|
||||
data.teamList.put(courseUnitId, Team(
|
||||
profileId,
|
||||
courseUnitId,
|
||||
"$classType $groupNumber ($courseId)",
|
||||
"${profile?.studentClassName} $classTypeId$groupNumber - $courseName",
|
||||
2,
|
||||
"${data.schoolId}:${courseId} $classTypeId$groupNumber",
|
||||
-1,
|
||||
|
@ -5,7 +5,6 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
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_TERMS
|
||||
@ -32,7 +31,7 @@ class UsosApiTerms(
|
||||
responseType = ResponseType.ARRAY,
|
||||
) { json, response ->
|
||||
if (!processResponse(json)) {
|
||||
data.error(UsosApiCourses.TAG, ERROR_USOS_API_INCOMPLETE_RESPONSE, response)
|
||||
data.error(TAG, ERROR_USOS_API_INCOMPLETE_RESPONSE, response)
|
||||
return@apiRequest
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2022-10-16.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.api
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
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_TIMETABLE
|
||||
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.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class UsosApiTimetable(
|
||||
override val data: DataUsos,
|
||||
override val lastSync: Long?,
|
||||
val onSuccess: (endpointId: Int) -> Unit,
|
||||
) : UsosApi(data, lastSync) {
|
||||
companion object {
|
||||
const val TAG = "UsosApiTimetable"
|
||||
}
|
||||
|
||||
init {
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
if (Date.getToday().weekDay > 4)
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
|
||||
val weekStart = data.arguments
|
||||
?.getString("weekStart")
|
||||
?.let { Date.fromY_m_d(it) }
|
||||
?: currentWeekStart
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
apiRequest<JsonArray>(
|
||||
tag = TAG,
|
||||
service = "tt/user",
|
||||
params = mapOf(
|
||||
"start" to weekStart.stringY_m_d,
|
||||
"days" to 7,
|
||||
),
|
||||
fields = listOf(
|
||||
"type",
|
||||
"start_time",
|
||||
"end_time",
|
||||
"unit_id",
|
||||
"course_id",
|
||||
"course_name",
|
||||
"lecturer_ids",
|
||||
"building_id",
|
||||
"room_number",
|
||||
"classtype_id",
|
||||
"group_number",
|
||||
),
|
||||
responseType = ResponseType.ARRAY,
|
||||
) { json, response ->
|
||||
if (!processResponse(json, weekStart..weekEnd)) {
|
||||
data.error(TAG, ERROR_USOS_API_INCOMPLETE_RESPONSE, response)
|
||||
return@apiRequest
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
data.setSyncNext(ENDPOINT_USOS_API_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess(ENDPOINT_USOS_API_TIMETABLE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processResponse(json: JsonArray, syncRange: ClosedRange<Date>): Boolean {
|
||||
val foundDates = mutableSetOf<Date>()
|
||||
|
||||
for (activity in json.asJsonObjectList()) {
|
||||
val type = activity.getString("type")
|
||||
if (type !in listOf("classgroup", "classgroup2"))
|
||||
continue
|
||||
|
||||
val startTime = activity.getString("start_time") ?: continue
|
||||
val endTime = activity.getString("end_time") ?: continue
|
||||
val unitId = activity.getLong("unit_id", -1)
|
||||
val courseName = activity.getLangString("course_name") ?: continue
|
||||
val courseId = activity.getString("course_id") ?: continue
|
||||
val lecturerIds = activity.getJsonArray("lecturer_ids")?.map { it.asLong }
|
||||
val buildingId = activity.getString("building_id")
|
||||
val roomNumber = activity.getString("room_number")
|
||||
val classTypeId = activity.getString("classtype_id")
|
||||
val groupNumber = activity.getString("group_number")
|
||||
|
||||
val lesson = Lesson(profileId, -1).also {
|
||||
it.type = Lesson.TYPE_NORMAL
|
||||
it.date = Date.fromY_m_d(startTime)
|
||||
it.startTime = Time.fromY_m_d_H_m_s(startTime)
|
||||
it.endTime = Time.fromY_m_d_H_m_s(endTime)
|
||||
it.subjectId = data.getSubject(
|
||||
id = null,
|
||||
name = courseName,
|
||||
shortName = courseId,
|
||||
).id
|
||||
it.teacherId = lecturerIds?.firstOrNull() ?: -1L
|
||||
it.teamId = unitId
|
||||
val groupName = classTypeId?.plus(groupNumber)?.let { s -> "($s)" }
|
||||
it.classroom = "$buildingId / $roomNumber ${groupName ?: ""}"
|
||||
it.id = it.buildId()
|
||||
}
|
||||
lesson.date?.let { foundDates += it }
|
||||
|
||||
val seen = profile?.empty != false || lesson.date!! < Date.getToday()
|
||||
data.lessonList.add(lesson)
|
||||
if (lesson.type != Lesson.TYPE_NORMAL)
|
||||
data.metadataList += Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lesson.id,
|
||||
seen,
|
||||
seen,
|
||||
)
|
||||
}
|
||||
|
||||
val notFoundDates = syncRange.asSequence() - foundDates
|
||||
for (date in notFoundDates) {
|
||||
data.lessonList += Lesson(profileId, date.value.toLong()).also {
|
||||
it.type = Lesson.TYPE_NO_LESSONS
|
||||
it.date = date
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
@ -7,9 +7,10 @@ package pl.szczodrzynski.edziennik.ext
|
||||
import android.content.Context
|
||||
import im.wangchao.mhttp.Response
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
const val MINUTE = 60L
|
||||
const val HOUR = 60L*MINUTE
|
||||
@ -115,3 +116,11 @@ fun Context.getSyncInterval(interval: Int): String {
|
||||
""
|
||||
return hoursText?.plus(" $minutesText") ?: minutesText
|
||||
}
|
||||
|
||||
fun ClosedRange<Date>.asSequence(): Sequence<Date> = sequence {
|
||||
val date = this@asSequence.start.clone()
|
||||
while (date in this@asSequence) {
|
||||
yield(date.clone())
|
||||
date.stepForward(0, 0, 1)
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,11 @@ import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_USOS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
@ -40,8 +43,8 @@ import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.utils.mutableLazy
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
@ -82,7 +85,7 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
startHour = startHour,
|
||||
endHour = endHour,
|
||||
dividerHeight = 1.dp,
|
||||
halfHourHeight = 60.dp,
|
||||
halfHourHeight = if (app.profile.loginStoreType == LOGIN_TYPE_USOS) 45.dp else 30.dp,
|
||||
hourDividerColor = R.attr.hourDividerColor.resolveAttr(context),
|
||||
halfHourDividerColor = R.attr.halfHourDividerColor.resolveAttr(context),
|
||||
hourLabelWidth = 40.dp,
|
||||
@ -184,11 +187,18 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
val lessonsActual = lessons.filter { it.type != Lesson.TYPE_NO_LESSONS }
|
||||
|
||||
val minStartHour = lessonsActual.minOf { it.displayStartTime?.hour ?: DEFAULT_END_HOUR }
|
||||
val maxEndHour = lessonsActual.maxOf { it.displayEndTime?.hour?.plus(1) ?: DEFAULT_START_HOUR }
|
||||
|
||||
if (profileConfig.timetableTrimHourRange) {
|
||||
dayViewDelegate.deinitialize()
|
||||
// end/start defaults are swapped on purpose
|
||||
startHour = lessonsActual.minOf { it.displayStartTime?.hour ?: DEFAULT_END_HOUR }
|
||||
endHour = lessonsActual.maxOf { it.displayEndTime?.hour?.plus(1) ?: DEFAULT_START_HOUR }
|
||||
startHour = minStartHour
|
||||
endHour = maxEndHour
|
||||
} else if (startHour > minStartHour || endHour < maxEndHour) {
|
||||
dayViewDelegate.deinitialize()
|
||||
startHour = min(startHour, minStartHour)
|
||||
endHour = max(endHour, maxEndHour)
|
||||
}
|
||||
|
||||
b.scrollView.isVisible = true
|
||||
@ -377,9 +387,8 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
// The day view needs the event time ranges in the start minute/end minute format,
|
||||
// so calculate those here
|
||||
val startMinute = 60 * (lesson.displayStartTime?.hour
|
||||
?: 0) + (lesson.displayStartTime?.minute ?: 0)
|
||||
val endMinute = startMinute + 45
|
||||
val startMinute = 60 * startTime.hour + startTime.minute
|
||||
val endMinute = 60 * endTime.hour + endTime.minute
|
||||
eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute))
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user