mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-18 12:56:45 -06:00
[APIv2] Implement Grades remove model. Implement Librus first login.
This commit is contained in:
parent
e91b4fcf8b
commit
f79263e628
@ -19,11 +19,13 @@ const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?clie
|
||||
const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action"
|
||||
const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token"
|
||||
|
||||
const val LIBRUS_ACCOUNT_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts/fresh/" // + login
|
||||
const val LIBRUS_ACCOUNTS_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts"
|
||||
const val LIBRUS_ACCOUNT_URL = "/v2/SynergiaAccounts/fresh/" // + login
|
||||
const val LIBRUS_ACCOUNTS_URL = "/v2/SynergiaAccounts"
|
||||
|
||||
/** https://api.librus.pl/2.0 */
|
||||
const val LIBRUS_API_URL = "https://api.librus.pl/2.0"
|
||||
/** https://portal.librus.pl/api */
|
||||
const val LIBRUS_PORTAL_URL = "https://portal.librus.pl/api"
|
||||
/** https://api.librus.pl/OAuth/Token */
|
||||
const val LIBRUS_API_TOKEN_URL = "https://api.librus.pl/OAuth/Token"
|
||||
/** https://api.librus.pl/OAuth/TokenJST */
|
||||
|
@ -46,6 +46,8 @@ const val ERROR_PROFILE_MISSING = 105
|
||||
const val ERROR_INVALID_LOGIN_MODE = 110
|
||||
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
|
||||
|
||||
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
|
||||
|
||||
const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120
|
||||
const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121
|
||||
const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124
|
||||
|
@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
@ -62,7 +63,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
// TODO
|
||||
LibrusFirstLogin(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
|
@ -17,7 +17,7 @@ import java.net.HttpURLConnection.*
|
||||
|
||||
open class LibrusApi(open val data: DataLibrus) {
|
||||
companion object {
|
||||
const val TAG = "LibrusApi"
|
||||
private const val TAG = "LibrusApi"
|
||||
}
|
||||
|
||||
val profileId
|
||||
|
@ -0,0 +1,101 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection
|
||||
|
||||
open class LibrusPortal(open val data: DataLibrus) {
|
||||
companion object {
|
||||
private const val TAG = "LibrusPortal"
|
||||
}
|
||||
|
||||
val profileId
|
||||
get() = data.profile?.id ?: -1
|
||||
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
fun portalGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject, response: Response?) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Portal - $LIBRUS_PORTAL_URL$endpoint")
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (json == null) {
|
||||
data.error(ApiError(tag, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
val error = if (response?.code() == 200) null else
|
||||
json.getString("reason") ?:
|
||||
json.getString("message") ?:
|
||||
json.getString("hint") ?:
|
||||
json.getString("Code")
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
"requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED
|
||||
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED
|
||||
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
|
||||
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
|
||||
else -> ERROR_LIBRUS_PORTAL_OTHER
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(tag, errorCode)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
}
|
||||
if (response?.code() == HttpURLConnection.HTTP_OK) {
|
||||
try {
|
||||
onSuccess(json, response)
|
||||
} catch (e: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
data.error(ApiError(tag, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN)
|
||||
.withResponse(response)
|
||||
.withThrowable(e)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
|
||||
} else {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url(LIBRUS_PORTAL_URL + endpoint)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addHeader("Authorization", "Bearer ${data.portalAccessToken}")
|
||||
.apply {
|
||||
when (method) {
|
||||
GET -> get()
|
||||
POST -> post()
|
||||
}
|
||||
if (payload != null)
|
||||
setJsonBody(payload)
|
||||
}
|
||||
.allowErrorCode(HttpURLConnection.HTTP_NOT_FOUND)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_GONE)
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT
|
||||
import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNTS_URL
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusPortal
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_LIBRUS_DISCONNECTED
|
||||
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_SYNERGIA_NOT_ACTIVATED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
import pl.szczodrzynski.edziennik.getJsonArray
|
||||
import pl.szczodrzynski.edziennik.getLong
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
|
||||
class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "LibrusFirstLogin"
|
||||
}
|
||||
|
||||
private val portal = LibrusPortal(data)
|
||||
private val api = LibrusApi(data)
|
||||
private val profileList = mutableListOf<Profile>()
|
||||
|
||||
init {
|
||||
if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) {
|
||||
// email login: use Portal for account list
|
||||
LibrusLoginPortal(data) {
|
||||
portal.portalGet(TAG, LIBRUS_ACCOUNTS_URL) { json, response ->
|
||||
val accounts = json.getJsonArray("accounts")
|
||||
|
||||
if (accounts == null || accounts.size() < 1) {
|
||||
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
return@portalGet
|
||||
}
|
||||
val accountDataTime = json.getLong("lastModification")
|
||||
val accountIds = mutableListOf<Int>()
|
||||
val accountLogins = mutableListOf<String>()
|
||||
val accountTokens = mutableListOf<String>()
|
||||
val accountNamesLong = mutableListOf<String>()
|
||||
val accountNamesShort = mutableListOf<String>()
|
||||
|
||||
for (accountEl in accounts) {
|
||||
val account = accountEl.asJsonObject
|
||||
|
||||
val state = account.getString("state")
|
||||
when (state) {
|
||||
"requiring_an_action" -> CODE_LIBRUS_DISCONNECTED
|
||||
"need-activation" -> CODE_SYNERGIA_NOT_ACTIVATED
|
||||
else -> null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return@portalGet
|
||||
}
|
||||
|
||||
accountIds.add(account.getInt("id") ?: continue)
|
||||
accountLogins.add(account.getString("login") ?: continue)
|
||||
accountTokens.add(account.getString("accessToken") ?: continue)
|
||||
accountNamesLong.add(account.getString("studentName") ?: continue)
|
||||
val nameParts = account.getString("studentName")?.split(" ") ?: continue
|
||||
accountNamesShort.add(nameParts[0] + " " + nameParts[1][0] + ".")
|
||||
}
|
||||
|
||||
for (index in accountIds.indices) {
|
||||
val newProfile = Profile()
|
||||
newProfile.studentNameLong = accountNamesLong[index]
|
||||
newProfile.studentNameShort = accountNamesShort[index]
|
||||
newProfile.name = newProfile.studentNameLong
|
||||
newProfile.subname = data.portalEmail
|
||||
newProfile.empty = true
|
||||
newProfile.putStudentData("accountId", accountIds[index])
|
||||
newProfile.putStudentData("accountLogin", accountLogins[index])
|
||||
newProfile.putStudentData("accountToken", accountTokens[index])
|
||||
newProfile.putStudentData("accountTokenTime", accountDataTime ?: 0)
|
||||
profileList.add(newProfile)
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// synergia or JST login: use Api for account info
|
||||
LibrusLoginApi(data) {
|
||||
api.apiGet(TAG, "Me") { json ->
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,14 +9,15 @@ import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.getUnixDate
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection.*
|
||||
import java.net.HttpURLConnection.HTTP_BAD_REQUEST
|
||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||
|
||||
class LibrusLoginApi {
|
||||
companion object {
|
||||
@ -31,7 +32,7 @@ class LibrusLoginApi {
|
||||
this.data = data
|
||||
this.onSuccess = onSuccess
|
||||
|
||||
if (data.profile == null) {
|
||||
if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL && data.profile == null) {
|
||||
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
|
||||
return
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusPortal
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection.*
|
||||
|
||||
class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusPortal(data) {
|
||||
companion object {
|
||||
private const val TAG = "SynergiaTokenExtractor"
|
||||
}
|
||||
@ -44,7 +41,7 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
private fun synergiaAccount(): Boolean {
|
||||
|
||||
val accountLogin = data.apiLogin ?: return false
|
||||
val accessToken = data.portalAccessToken ?: return false
|
||||
data.portalAccessToken ?: return false
|
||||
|
||||
d(TAG, "Request: Librus/SynergiaTokenExtractor - $LIBRUS_ACCOUNT_URL$accountLogin")
|
||||
|
||||
@ -63,77 +60,14 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
|
||||
// TODO remove this
|
||||
data.profile?.studentNameLong = json.getString("studentName")
|
||||
val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray()
|
||||
val nameParts = json.getString("studentName")?.split(" ")
|
||||
data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0)
|
||||
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (json == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
val error = if (response?.code() == 200) null else
|
||||
json.getString("reason") ?:
|
||||
json.getString("message") ?:
|
||||
json.getString("hint") ?:
|
||||
json.getString("Code")
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
"requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED
|
||||
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED
|
||||
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
|
||||
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
|
||||
else -> ERROR_LIBRUS_PORTAL_OTHER
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
}
|
||||
if (response?.code() == HTTP_OK) {
|
||||
try {
|
||||
onSuccess(json, response)
|
||||
} catch (e: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
data.error(ApiError(TAG, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN)
|
||||
.withResponse(response)
|
||||
.withThrowable(e)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
|
||||
} else {
|
||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url(LIBRUS_ACCOUNT_URL + accountLogin)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addHeader("Authorization", "Bearer $accessToken")
|
||||
.get()
|
||||
.allowErrorCode(HTTP_NOT_FOUND)
|
||||
.allowErrorCode(HTTP_FORBIDDEN)
|
||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HTTP_GONE)
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
portalGet(TAG, LIBRUS_ACCOUNT_URL+accountLogin, onSuccess = onSuccess)
|
||||
return true
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un
|
||||
val profile = Profile()
|
||||
profile.studentNameLong = studentNamesLong[index]
|
||||
profile.studentNameShort = studentNamesShort[index]
|
||||
profile.name = profile.studentNameLong + " (APIv2)"
|
||||
profile.name = profile.studentNameLong
|
||||
profile.subname = data.loginUsername
|
||||
profile.empty = true
|
||||
profile.putStudentData("studentId", studentIds[index])
|
||||
|
@ -190,6 +190,11 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
db.gradeCategoryDao().clear(profileId)
|
||||
db.gradeCategoryDao().addAll(gradeCategories.values())
|
||||
|
||||
gradesToRemove?.let { it ->
|
||||
it.removeAll?.let { _ -> db.gradeDao().clear(profileId) }
|
||||
it.removeSemester?.let { semester -> db.gradeDao().clearForSemester(profileId, semester) }
|
||||
}
|
||||
|
||||
if (lessonList.isNotEmpty()) {
|
||||
db.lessonDao().clear(profile.id)
|
||||
db.lessonDao().addAll(lessonList)
|
||||
@ -197,7 +202,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
if (lessonChangeList.isNotEmpty())
|
||||
db.lessonChangeDao().addAll(lessonChangeList)
|
||||
if (gradeList.isNotEmpty()) {
|
||||
db.gradeDao().clear(profile.id)
|
||||
db.gradeDao().addAll(gradeList)
|
||||
}
|
||||
if (eventList.isNotEmpty()) {
|
||||
|
@ -7,10 +7,15 @@ package pl.szczodrzynski.edziennik.api.v2.models
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class DataRemoveModel {
|
||||
var removeAll: Boolean? = null
|
||||
var removeSemester: Int? = null
|
||||
var removeDateFrom: Date? = null
|
||||
var removeDateTo: Date? = null
|
||||
|
||||
constructor() {
|
||||
this.removeAll = true
|
||||
}
|
||||
|
||||
constructor(semester: Int) {
|
||||
this.removeSemester = semester
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user