mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-18 12:56:45 -06:00
[APIv2/Idziennik] Fix Attendance and Exams. Add Lucky number.
This commit is contained in:
parent
d6d73b19ec
commit
f2b3603531
@ -58,6 +58,10 @@ const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcz
|
||||
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"
|
||||
const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci"
|
||||
|
||||
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
|
||||
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"
|
||||
const val IDZIENNIK_API_CURRENT_REGISTER = "Uczniowie/\$STUDENT_ID/AktualnyDziennik"
|
||||
|
||||
val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT
|
||||
|
||||
const val VULCAN_API_USER_AGENT = "MobileUserAgent"
|
||||
|
@ -133,7 +133,6 @@ const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 403
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_MAINTENANCE = 404
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_SERVER_ERROR = 405
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_API_ERROR = 406 /* {"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""} */
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_OTHER = 410
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_API_NO_ACCESS = 411 /* {"d":{"__type":"mds.Web.mod_komunikator.WS_mod_wiadomosci+detailWiadomosci","Wiadomosc":{"_recordId":0,"DataNadania":null,"DataOdczytania":null,"Nadawca":null,"ListaOdbiorcow":[],"Tytul":null,"Text":null,"ListaZal":[]},"Bledy":{"__type":"mds.Module.Globalne+sBledy","CzyJestBlad":true,"ListaBledow":["Nie masz dostępu do tych zasobów!"],"ListaKodowBledow":[]},"czyJestWiecej":false}} */
|
||||
const val ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION = 420
|
||||
@ -146,6 +145,8 @@ const val ERROR_IDZIENNIK_WEB_SERVER_ERROR = 433
|
||||
const val ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 434
|
||||
const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440
|
||||
const val ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA = 441
|
||||
const val ERROR_IDZIENNIK_API_ACCESS_DENIED = 450
|
||||
const val ERROR_IDZIENNIK_API_OTHER = 451
|
||||
|
||||
const val ERROR_TEMPLATE_WEB_OTHER = 801
|
||||
|
||||
@ -160,3 +161,4 @@ const val EXCEPTION_NOTIFY_AND_SYNC = 910
|
||||
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
|
||||
|
@ -40,24 +40,24 @@ object Regexes {
|
||||
|
||||
|
||||
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(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_ERROR by lazy {
|
||||
"id=\"spanErrorMessage\">(.*?)</".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
"""id="spanErrorMessage">(.*?)</""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME by lazy {
|
||||
"Imię i nazwisko:.+?\">(.+?)</div>".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
"""Imię i nazwisko:.+?">(.+?)</div>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
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 {
|
||||
"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(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
|
||||
"<select.*?name=\"ctl00\\\$dxComboUczniowie\".*?</select>".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
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(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
fun isWebLoginValid() = loginExpiryTime-30 > currentTimeUnix() && webSessionId.isNotNullNorEmpty() && webAuth.isNotNullNorEmpty()
|
||||
fun isApiLoginValid() = loginExpiryTime-30 > currentTimeUnix() && apiBearer.isNotNullNorEmpty()
|
||||
fun isApiLoginValid() = apiExpiryTime-30 > currentTimeUnix() && apiBearer.isNotNullNorEmpty()
|
||||
|
||||
override fun satisfyLoginMethods() {
|
||||
loginMethods.clear()
|
||||
@ -46,6 +46,11 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
get() { mLoginExpiryTime = mLoginExpiryTime ?: loginStore.getLoginData("loginExpiryTime", 0L); return mLoginExpiryTime ?: 0L }
|
||||
set(value) { loginStore.putLoginData("loginExpiryTime", value); mLoginExpiryTime = value }
|
||||
|
||||
private var mApiExpiryTime: Long? = null
|
||||
var apiExpiryTime: Long
|
||||
get() { mApiExpiryTime = mApiExpiryTime ?: loginStore.getLoginData("apiExpiryTime", 0L); return mApiExpiryTime ?: 0L }
|
||||
set(value) { loginStore.putLoginData("apiExpiryTime", value); mApiExpiryTime = value }
|
||||
|
||||
/* __ __ _
|
||||
\ \ / / | |
|
||||
\ \ /\ / /__| |__
|
||||
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik.data
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import java.net.HttpURLConnection
|
||||
|
||||
open class IdziennikApi(open val data: DataIdziennik) {
|
||||
companion object {
|
||||
const val TAG = "IdziennikApi"
|
||||
}
|
||||
|
||||
val profileId
|
||||
get() = data.profile?.id ?: -1
|
||||
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
fun apiGet(tag: String, endpointTemplate: String, method: Int = GET, parameters: Map<String, Any> = emptyMap(), onSuccess: (json: JsonElement) -> Unit) {
|
||||
val endpoint = endpointTemplate.replace("\$STUDENT_ID", data.studentId ?: "")
|
||||
Utils.d(tag, "Request: Idziennik/API - $IDZIENNIK_API_URL/$endpoint")
|
||||
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
if (text == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
val json = try {
|
||||
JsonParser().parse(text)
|
||||
} catch (_: Exception) { null }
|
||||
|
||||
var error: String? = null
|
||||
if (json == null) {
|
||||
error = text
|
||||
}
|
||||
else if (json is JsonObject) {
|
||||
error = if (response?.code() == 200) null else
|
||||
json.getString("message") ?: json.toString()
|
||||
}
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
"Authorization has been denied for this request." -> ERROR_IDZIENNIK_API_ACCESS_DENIED
|
||||
else -> ERROR_IDZIENNIK_API_OTHER
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(tag, errorCode)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
onSuccess(json!!)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_IDZIENNIK_API_REQUEST)
|
||||
.withResponse(response)
|
||||
.withThrowable(e)
|
||||
.withApiResponse(text))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("$IDZIENNIK_API_URL/$endpoint")
|
||||
.userAgent(IDZIENNIK_API_USER_AGENT)
|
||||
.addHeader("Authorization", "Bearer ${data.apiBearer}")
|
||||
.apply {
|
||||
when (method) {
|
||||
GET -> get()
|
||||
POST -> {
|
||||
postJson()
|
||||
val json = JsonObject()
|
||||
parameters.map { (name, value) ->
|
||||
when (value) {
|
||||
is JsonObject -> json.add(name, value)
|
||||
is JsonArray -> json.add(name, value)
|
||||
is String -> json.addProperty(name, value)
|
||||
is Int -> json.addProperty(name, value)
|
||||
is Long -> json.addProperty(name, value)
|
||||
is Float -> json.addProperty(name, value)
|
||||
is Char -> json.addProperty(name, value)
|
||||
}
|
||||
}
|
||||
setJsonBody(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_INTERNAL_ERROR)
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data
|
||||
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiCurrentRegister
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.*
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
|
||||
@ -65,6 +66,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
|
||||
IdziennikWebAttendance(data) { onSuccess() }
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
|
||||
IdziennikApiCurrentRegister(data) { onSuccess() }
|
||||
}
|
||||
else -> onSuccess()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.DAY
|
||||
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_API_CURRENT_REGISTER
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class IdziennikApiCurrentRegister(override val data: DataIdziennik,
|
||||
val onSuccess: () -> Unit) : IdziennikApi(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikApiCurrentRegister"
|
||||
}
|
||||
|
||||
init {
|
||||
data.profile?.luckyNumber = -1
|
||||
data.profile?.luckyNumberDate = null
|
||||
|
||||
apiGet(TAG, IDZIENNIK_API_CURRENT_REGISTER) { json ->
|
||||
if (json !is JsonObject) {
|
||||
onSuccess()
|
||||
return@apiGet
|
||||
}
|
||||
|
||||
var nextSync = System.currentTimeMillis() + 14*DAY*1000
|
||||
|
||||
val settings = json.getJsonObject("ustawienia")?.apply {
|
||||
profile?.dateSemester1Start = getString("poczatekSemestru1")?.let { Date.fromY_m_d(it) }
|
||||
profile?.dateSemester2Start = getString("koniecSemestru1")?.let { Date.fromY_m_d(it).stepForward(0, 0, 1) }
|
||||
profile?.dateYearEnd = getString("koniecSemestru2")?.let { Date.fromY_m_d(it) }
|
||||
}
|
||||
|
||||
json.getInt("szczesliwyNumerek")?.let { luckyNumber ->
|
||||
val luckyNumberDate = Date.getToday()
|
||||
settings.getString("godzinaPublikacjiSzczesliwegoLosu")
|
||||
?.let { Time.fromH_m(it) }
|
||||
?.let { publishTime ->
|
||||
val now = Time.getNow()
|
||||
if (publishTime.value < 150000 && now.value < publishTime.value) {
|
||||
nextSync = luckyNumberDate.combineWith(publishTime)
|
||||
luckyNumberDate.stepForward(0, 0, -1) // the lucky number is still for yesterday
|
||||
}
|
||||
else if (publishTime.value >= 150000 && now.value > publishTime.value) {
|
||||
luckyNumberDate.stepForward(0, 0, 1) // the lucky number is already for tomorrow
|
||||
nextSync = luckyNumberDate.combineWith(publishTime)
|
||||
}
|
||||
else if (publishTime.value < 150000) {
|
||||
nextSync = luckyNumberDate
|
||||
.clone()
|
||||
.stepForward(0, 0, 1)
|
||||
.combineWith(publishTime)
|
||||
}
|
||||
else {
|
||||
nextSync = luckyNumberDate.combineWith(publishTime)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val luckyNumberObject = LuckyNumber(
|
||||
data.profileId,
|
||||
Date.getToday(),
|
||||
luckyNumber
|
||||
)
|
||||
|
||||
data.luckyNumberList.add(luckyNumberObject)
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LUCKY_NUMBER,
|
||||
luckyNumberObject.date.value.toLong(),
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
data.setSyncNext(ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER, syncAt = nextSync)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -25,13 +25,14 @@ class IdziennikWebAttendance(override val data: DataIdziennik,
|
||||
private const val TAG = "IdziennikWebAttendance"
|
||||
}
|
||||
|
||||
private var attendanceYear = Date.getToday().year
|
||||
private var attendanceMonth = Date.getToday().month
|
||||
private var attendancePrevMonthChecked = false
|
||||
|
||||
init {
|
||||
getAttendance()
|
||||
}
|
||||
|
||||
private var attendanceYear: Int = Date.getToday().year
|
||||
private var attendanceMonth: Int = Date.getToday().month
|
||||
private var attendancePrevMonthChecked = false
|
||||
private fun getAttendance() {
|
||||
webApiGet(TAG, IDZIENNIK_WEB_ATTENDANCE, mapOf(
|
||||
"idPozDziennika" to data.registerId,
|
||||
|
@ -24,14 +24,15 @@ class IdziennikWebExams(override val data: DataIdziennik,
|
||||
private const val TAG = "IdziennikWebExams"
|
||||
}
|
||||
|
||||
init {
|
||||
getExams()
|
||||
}
|
||||
|
||||
private var examsYear = Date.getToday().year
|
||||
private var examsMonth = Date.getToday().month
|
||||
private var examsMonthsChecked = 0
|
||||
private var examsNextMonthChecked = false // TO DO temporary // no more // idk
|
||||
|
||||
init {
|
||||
getExams()
|
||||
}
|
||||
|
||||
private fun getExams() {
|
||||
val param = JsonObject()
|
||||
param.addProperty("strona", 1)
|
||||
|
@ -8,6 +8,8 @@ import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.HOUR
|
||||
import pl.szczodrzynski.edziennik.MINUTE
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
@ -65,7 +67,8 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
|
||||
data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
|
||||
data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
|
||||
data.loginExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */
|
||||
data.loginExpiryTime = response.getUnixDate() + 45 * MINUTE
|
||||
data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */
|
||||
return@run null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
|
@ -324,7 +324,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null) {
|
||||
fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null, syncAt: Long? = null) {
|
||||
EndpointTimer(profile?.id ?: -1, endpointId).apply {
|
||||
syncedNow()
|
||||
|
||||
@ -334,6 +334,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
else
|
||||
syncIn(syncIn)
|
||||
}
|
||||
if (syncAt != null) {
|
||||
nextSync = syncAt
|
||||
}
|
||||
if (viewId != null)
|
||||
syncWhenView(viewId)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user