Changeset View
Changeset View
Standalone View
Standalone View
src/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
/* | |||||
BusTO - Backend components | |||||
Copyright (C) 2021 Fabio Mazza | |||||
This program is free software: you can redistribute it and/or modify | |||||
it under the terms of the GNU General Public License as published by | |||||
the Free Software Foundation, either version 3 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU General Public License for more details. | |||||
You should have received a copy of the GNU General Public License | |||||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
package it.reyboz.bustorino.backend.mato | package it.reyboz.bustorino.backend.mato | ||||
import android.content.Context | import android.content.Context | ||||
import android.util.Log | import android.util.Log | ||||
import com.android.volley.toolbox.RequestFuture | import com.android.volley.toolbox.RequestFuture | ||||
import it.reyboz.bustorino.backend.* | import it.reyboz.bustorino.backend.* | ||||
import org.json.JSONObject | import org.json.JSONObject | ||||
import java.util.* | import java.util.* | ||||
import java.util.concurrent.TimeUnit | import java.util.concurrent.TimeUnit | ||||
import java.util.concurrent.atomic.AtomicReference | import java.util.concurrent.atomic.AtomicReference | ||||
import java.util.concurrent.TimeoutException | import java.util.concurrent.TimeoutException | ||||
import java.util.concurrent.ExecutionException | import java.util.concurrent.ExecutionException | ||||
open class MatoAPIFetcher : ArrivalsFetcher { | open class MatoAPIFetcher(val minNumPassaggi: Int) : ArrivalsFetcher { | ||||
var appContext: Context? = null | var appContext: Context? = null | ||||
set(value) { | set(value) { | ||||
field = value!!.applicationContext | field = value!!.applicationContext | ||||
} | } | ||||
constructor(): this(3) | |||||
override fun ReadArrivalTimesAll(stopID: String?, res: AtomicReference<Fetcher.Result>?): Palina { | override fun ReadArrivalTimesAll(stopID: String?, res: AtomicReference<Fetcher.Result>?): Palina { | ||||
stopID!! | stopID!! | ||||
val future = RequestFuture.newFuture<Palina>() | val future = RequestFuture.newFuture<Palina>() | ||||
val now = Calendar.getInstance().time; | val now = Calendar.getInstance().time | ||||
var numMinutes = 30; | var numMinutes = 30 | ||||
var palina = Palina(stopID) | var palina = Palina(stopID) | ||||
var numPassaggi = 0 | var numPassaggi = 0 | ||||
var trials = 0 | var trials = 0 | ||||
while (numPassaggi < 2 && trials < 4) { | while (numPassaggi < minNumPassaggi && trials < 4) { | ||||
numMinutes += 15 | numMinutes += 15 | ||||
val request = MapiArrivalRequest(stopID, now, numMinutes * 60, 10, future, future) | val request = MapiArrivalRequest(stopID, now, numMinutes * 60, 10, res, future, future) | ||||
if (appContext == null || res == null) { | if (appContext == null || res == null) { | ||||
Log.e("BusTO:MatoAPIFetcher", "ERROR: Given null context or null result ref") | Log.e("BusTO:MatoAPIFetcher", "ERROR: Given null context or null result ref") | ||||
return Palina(stopID) | return Palina(stopID) | ||||
} | } | ||||
val requestQueue = NetworkVolleyManager.getInstance(appContext).requestQueue | val requestQueue = NetworkVolleyManager.getInstance(appContext).requestQueue | ||||
request.setTag(VOLLEY_TAG) | request.setTag(getVolleyReqTag(QueryType.ARRIVALS)) | ||||
requestQueue.add(request) | requestQueue.add(request) | ||||
try { | try { | ||||
val palinaResult = future.get(5, TimeUnit.SECONDS) | val palinaResult = future.get(5, TimeUnit.SECONDS) | ||||
if (palinaResult!=null) { | if (palinaResult!=null) { | ||||
palina = palinaResult | palina = palinaResult | ||||
if (palina.totalNumberOfPassages > 0) { | |||||
res.set(Fetcher.Result.OK) | |||||
} else res.set(Fetcher.Result.EMPTY_RESULT_SET) | |||||
numPassaggi = palina.totalNumberOfPassages | numPassaggi = palina.totalNumberOfPassages | ||||
} else{ | |||||
res.set(Fetcher.Result.EMPTY_RESULT_SET) | |||||
} | } | ||||
} catch (e: InterruptedException) { | } catch (e: InterruptedException) { | ||||
e.printStackTrace() | e.printStackTrace() | ||||
res.set(Fetcher.Result.PARSER_ERROR) | res.set(Fetcher.Result.PARSER_ERROR) | ||||
} catch (e: ExecutionException) { | } catch (e: ExecutionException) { | ||||
e.printStackTrace() | e.printStackTrace() | ||||
if (res.get() == Fetcher.Result.OK) | |||||
res.set(Fetcher.Result.SERVER_ERROR) | res.set(Fetcher.Result.SERVER_ERROR) | ||||
} catch (e: TimeoutException) { | } catch (e: TimeoutException) { | ||||
res.set(Fetcher.Result.CONNECTION_ERROR) | res.set(Fetcher.Result.CONNECTION_ERROR) | ||||
e.printStackTrace() | e.printStackTrace() | ||||
} | } | ||||
trials++ | trials++ | ||||
} | } | ||||
Show All 10 Lines | companion object{ | ||||
const val DEBUG_TAG = "BusTO:MatoAPIFetcher" | const val DEBUG_TAG = "BusTO:MatoAPIFetcher" | ||||
val REQ_PARAMETERS = mapOf( | val REQ_PARAMETERS = mapOf( | ||||
"Content-Type" to "application/json; charset=utf-8", | "Content-Type" to "application/json; charset=utf-8", | ||||
"DNT" to "1", | "DNT" to "1", | ||||
"Host" to "mapi.5t.torino.it") | "Host" to "mapi.5t.torino.it") | ||||
fun getVolleyReqTag(type: QueryType): String{ | |||||
return when (type){ | |||||
QueryType.ALL_STOPS -> VOLLEY_TAG +"_AllStops" | |||||
QueryType.ARRIVALS -> VOLLEY_TAG+"_Arrivals" | |||||
} | |||||
} | |||||
/** | |||||
* Get stops from the MatoAPI, set [res] accordingly | |||||
*/ | |||||
fun getAllStopsGTT(context: Context, res: AtomicReference<Fetcher.Result>?): List<Palina>{ | |||||
val requestQueue = NetworkVolleyManager.getInstance(context).requestQueue | |||||
val future = RequestFuture.newFuture<List<Palina>>() | |||||
val request = VolleyAllStopsRequest(future, future) | |||||
request.tag = getVolleyReqTag(QueryType.ALL_STOPS) | |||||
requestQueue.add(request) | |||||
var palinaList:List<Palina> = mutableListOf<Palina>() | |||||
try { | |||||
palinaList = future.get(30, TimeUnit.SECONDS) | |||||
res?.set(Fetcher.Result.OK) | |||||
}catch (e: InterruptedException) { | |||||
e.printStackTrace() | |||||
res?.set(Fetcher.Result.PARSER_ERROR) | |||||
} catch (e: ExecutionException) { | |||||
e.printStackTrace() | |||||
res?.set(Fetcher.Result.SERVER_ERROR) | |||||
} catch (e: TimeoutException) { | |||||
res?.set(Fetcher.Result.CONNECTION_ERROR) | |||||
e.printStackTrace() | |||||
} | |||||
return palinaList | |||||
} | |||||
/* | |||||
fun makeRequest(type: QueryType?, variables: JSONObject) : String{ | fun makeRequest(type: QueryType?, variables: JSONObject) : String{ | ||||
type.let { | type.let { | ||||
val requestData = JSONObject() | val requestData = JSONObject() | ||||
when (it){ | when (it){ | ||||
QueryType.ARRIVALS ->{ | QueryType.ARRIVALS ->{ | ||||
requestData.put("operationName","AllStopsDirect") | requestData.put("operationName","AllStopsDirect") | ||||
requestData.put("variables", variables) | requestData.put("variables", variables) | ||||
requestData.put("query", QUERY_ARRIVALS) | requestData.put("query", MatoQueries.QUERY_ARRIVALS) | ||||
} | } | ||||
else -> { | else -> { | ||||
//TODO all other cases | //TODO all other cases | ||||
} | } | ||||
} | } | ||||
//todo make the request... | //todo make the request... | ||||
//https://pablobaxter.github.io/volley-docs/com/android/volley/toolbox/RequestFuture.html | //https://pablobaxter.github.io/volley-docs/com/android/volley/toolbox/RequestFuture.html | ||||
//https://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley | //https://stackoverflow.com/questions/16904741/can-i-do-a-synchronous-request-with-volley | ||||
} | } | ||||
return "" | return "" | ||||
} | } | ||||
*/ | |||||
fun parseStopJSON(jsonStop: JSONObject): Palina{ | fun parseStopJSON(jsonStop: JSONObject): Palina{ | ||||
val latitude = jsonStop.getDouble("lat") | val latitude = jsonStop.getDouble("lat") | ||||
val longitude = jsonStop.getDouble("lon") | val longitude = jsonStop.getDouble("lon") | ||||
val palina = Palina( | val palina = Palina( | ||||
jsonStop.getString("code"), | jsonStop.getString("code"), | ||||
jsonStop.getString("name"), | jsonStop.getString("name"), | ||||
null, null, latitude, longitude | null, null, latitude, longitude | ||||
) | ) | ||||
palina.gtfsID = jsonStop.getString("gtfsId") | palina.gtfsID = jsonStop.getString("gtfsId") | ||||
val routesStoppingJSON = jsonStop.getJSONArray("routes") | val routesStoppingJSON = jsonStop.getJSONArray("routes") | ||||
val baseRoutes = mutableListOf<Route>() | val baseRoutes = mutableListOf<Route>() | ||||
// get all the possible routes | |||||
for (i in 0 until routesStoppingJSON.length()){ | for (i in 0 until routesStoppingJSON.length()){ | ||||
val routeBaseInfo = routesStoppingJSON.getJSONObject(i) | val routeBaseInfo = routesStoppingJSON.getJSONObject(i) | ||||
val r = Route(routeBaseInfo.getString("shortName"), Route.Type.UNKNOWN,"") | val r = Route(routeBaseInfo.getString("shortName"), Route.Type.UNKNOWN,"") | ||||
r.gtfsId = routeBaseInfo.getString("gtfsId").trim() | r.gtfsId = routeBaseInfo.getString("gtfsId").trim() | ||||
baseRoutes.add(r) | baseRoutes.add(r) | ||||
} | } | ||||
if (jsonStop.has("desc")){ | |||||
palina.location = jsonStop.getString("desc") | |||||
} | |||||
//there is also "zoneId" which is the zone of the stop (0-> city, etc) | |||||
if(jsonStop.has("stoptimesForPatterns")) { | |||||
val routesStopTimes = jsonStop.getJSONArray("stoptimesForPatterns") | val routesStopTimes = jsonStop.getJSONArray("stoptimesForPatterns") | ||||
for (i in 0 until routesStopTimes.length()){ | for (i in 0 until routesStopTimes.length()) { | ||||
val patternJSON = routesStopTimes.getJSONObject(i) | val patternJSON = routesStopTimes.getJSONObject(i) | ||||
val mRoute = parseRouteStoptimesJSON(patternJSON) | val mRoute = parseRouteStoptimesJSON(patternJSON) | ||||
//val directionId = patternJSON.getJSONObject("pattern").getInt("directionId") | //val directionId = patternJSON.getJSONObject("pattern").getInt("directionId") | ||||
//TODO: use directionId | //TODO: use directionId | ||||
palina.addRoute(mRoute) | palina.addRoute(mRoute) | ||||
for (r in baseRoutes) { | for (r in baseRoutes) { | ||||
if (palina.gtfsID != null && r.gtfsId.equals(palina.gtfsID)) { | if (palina.gtfsID != null && r.gtfsId.equals(palina.gtfsID)) { | ||||
baseRoutes.remove(r) | baseRoutes.remove(r) | ||||
break | break | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
for (noArrivalRoute in baseRoutes){ | for (noArrivalRoute in baseRoutes){ | ||||
palina.addRoute(noArrivalRoute) | palina.addRoute(noArrivalRoute) | ||||
} | } | ||||
//val gtfsRoutes = mutableListOf<>() | //val gtfsRoutes = mutableListOf<>() | ||||
return palina | return palina | ||||
} | } | ||||
fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{ | fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{ | ||||
val patternJSON = jsonPatternWithStops.getJSONObject("pattern") | val patternJSON = jsonPatternWithStops.getJSONObject("pattern") | ||||
val routeJSON = patternJSON.getJSONObject("route"); | val routeJSON = patternJSON.getJSONObject("route") | ||||
val passaggiJSON = jsonPatternWithStops.getJSONArray("stoptimes") | val passaggiJSON = jsonPatternWithStops.getJSONArray("stoptimes") | ||||
val gtfsId = routeJSON.getString("gtfsId").trim() | val gtfsId = routeJSON.getString("gtfsId").trim() | ||||
val passages = mutableListOf<Passaggio>() | val passages = mutableListOf<Passaggio>() | ||||
for( i in 0 until passaggiJSON.length()){ | for( i in 0 until passaggiJSON.length()){ | ||||
val stoptime = passaggiJSON.getJSONObject(i) | val stoptime = passaggiJSON.getJSONObject(i) | ||||
val scheduledTime = stoptime.getInt("scheduledArrival") | val scheduledTime = stoptime.getInt("scheduledArrival") | ||||
val realtimeTime = stoptime.getInt("realtimeArrival") | val realtimeTime = stoptime.getInt("realtimeArrival") | ||||
Show All 15 Lines | companion object{ | ||||
patternJSON.getString("headsign"), | patternJSON.getString("headsign"), | ||||
routeType, | routeType, | ||||
passages, | passages, | ||||
) | ) | ||||
route.gtfsId = gtfsId | route.gtfsId = gtfsId | ||||
return route | return route | ||||
} | } | ||||
const val QUERY_ARRIVALS="""query AllStopsDirect( | |||||
${'$'}name: String | fun makeRequestParameters(requestName:String, variables: JSONObject, query: String): JSONObject{ | ||||
${'$'}startTime: Long | val data = JSONObject() | ||||
${'$'}timeRange: Int | data.put("operationName", requestName) | ||||
${'$'}numberOfDepartures: Int | data.put("variables", variables) | ||||
) { | data.put("query", query) | ||||
stops(name: ${'$'}name) { | return data | ||||
__typename | |||||
lat | |||||
lon | |||||
gtfsId | |||||
code | |||||
name | |||||
desc | |||||
wheelchairBoarding | |||||
routes { | |||||
__typename | |||||
gtfsId | |||||
shortName | |||||
} | |||||
stoptimesForPatterns( | |||||
startTime: ${'$'}startTime | |||||
timeRange: ${'$'}timeRange | |||||
numberOfDepartures: ${'$'}numberOfDepartures | |||||
) { | |||||
__typename | |||||
pattern { | |||||
__typename | |||||
headsign | |||||
directionId | |||||
route { | |||||
__typename | |||||
gtfsId | |||||
shortName | |||||
mode | |||||
} | |||||
} | |||||
stoptimes { | |||||
__typename | |||||
scheduledArrival | |||||
realtimeArrival | |||||
realtime | |||||
realtimeState | |||||
} | |||||
} | |||||
} | |||||
} | |||||
""" | |||||
} | } | ||||
} | |||||
enum class QueryType { | enum class QueryType { | ||||
ARRIVALS, | ARRIVALS, ALL_STOPS | ||||
} | } | ||||
} | } | ||||
No newline at end of file | No newline at end of file |
Public contents are in Creative Commons Attribution-ShareAlike 4.0 (CC-BY-SA) or GNU Free Documentation License (at your option) unless otherwise noted. · Contact / Register