mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-11-02 15:53:55 +01:00
Convert OkHttpJsonApiClient and CategoryApi to kotlin
This commit is contained in:
parent
33548fa57d
commit
79de03964e
7 changed files with 633 additions and 783 deletions
|
|
@ -52,12 +52,12 @@ class CampaignsPresenter @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
okHttpJsonApiClient.campaigns
|
okHttpJsonApiClient.getCampaigns()
|
||||||
.observeOn(mainThreadScheduler)
|
.observeOn(mainThreadScheduler)
|
||||||
.subscribeOn(ioScheduler)
|
.subscribeOn(ioScheduler)
|
||||||
.doOnSubscribe { disposable = it }
|
.doOnSubscribe { disposable = it }
|
||||||
.subscribe({ campaignResponseDTO ->
|
.subscribe({ campaignResponseDTO ->
|
||||||
val campaigns = campaignResponseDTO.campaigns?.toMutableList()
|
val campaigns = campaignResponseDTO?.campaigns?.toMutableList()
|
||||||
if (campaigns.isNullOrEmpty()) {
|
if (campaigns.isNullOrEmpty()) {
|
||||||
Timber.e("The campaigns list is empty")
|
Timber.e("The campaigns list is empty")
|
||||||
view!!.showCampaigns(null)
|
view!!.showCampaigns(null)
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
package fr.free.nrw.commons.mwapi;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
|
||||||
import fr.free.nrw.commons.category.CategoryItem;
|
|
||||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage;
|
|
||||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse;
|
|
||||||
import io.reactivex.Single;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import okhttp3.HttpUrl;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import okhttp3.ResponseBody;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates
|
|
||||||
* with nearby Commons categories. Parses the results using GSON to obtain a list of relevant
|
|
||||||
* categories. Note: that caller is responsible for executing the request() method on a background
|
|
||||||
* thread.
|
|
||||||
*/
|
|
||||||
public class CategoryApi {
|
|
||||||
|
|
||||||
private final OkHttpClient okHttpClient;
|
|
||||||
private final Gson gson;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public CategoryApi(final OkHttpClient okHttpClient, final Gson gson) {
|
|
||||||
this.okHttpClient = okHttpClient;
|
|
||||||
this.gson = gson;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Single<List<CategoryItem>> request(String coords) {
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
HttpUrl apiUrl = buildUrl(coords);
|
|
||||||
Timber.d("URL: %s", apiUrl.toString());
|
|
||||||
|
|
||||||
Request request = new Request.Builder().get().url(apiUrl).build();
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
ResponseBody body = response.body();
|
|
||||||
if (body == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
MwQueryResponse apiResponse = gson.fromJson(body.charStream(), MwQueryResponse.class);
|
|
||||||
Set<CategoryItem> categories = new LinkedHashSet<>();
|
|
||||||
if (apiResponse != null && apiResponse.query() != null && apiResponse.query().pages() != null) {
|
|
||||||
for (MwQueryPage page : apiResponse.query().pages()) {
|
|
||||||
if (page.categories() != null) {
|
|
||||||
for (MwQueryPage.Category category : page.categories()) {
|
|
||||||
categories.add(new CategoryItem(category.title().replace(CATEGORY_PREFIX, ""), "", "", false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new ArrayList<>(categories);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds URL with image coords for MediaWiki API calls
|
|
||||||
* Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2
|
|
||||||
*
|
|
||||||
* @param coords Coordinates to build query with
|
|
||||||
* @return URL for API query
|
|
||||||
*/
|
|
||||||
private HttpUrl buildUrl(final String coords) {
|
|
||||||
return HttpUrl
|
|
||||||
.parse(BuildConfig.WIKIMEDIA_API_HOST)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("action", "query")
|
|
||||||
.addQueryParameter("prop", "categories|coordinates|pageprops")
|
|
||||||
.addQueryParameter("format", "json")
|
|
||||||
.addQueryParameter("clshow", "!hidden")
|
|
||||||
.addQueryParameter("coprop", "type|name|dim|country|region|globe")
|
|
||||||
.addQueryParameter("codistancefrompoint", coords)
|
|
||||||
.addQueryParameter("generator", "geosearch")
|
|
||||||
.addQueryParameter("ggscoord", coords)
|
|
||||||
.addQueryParameter("ggsradius", "10000")
|
|
||||||
.addQueryParameter("ggslimit", "10")
|
|
||||||
.addQueryParameter("ggsnamespace", "6")
|
|
||||||
.addQueryParameter("ggsprop", "type|name|dim|country|region|globe")
|
|
||||||
.addQueryParameter("ggsprimary", "all")
|
|
||||||
.addQueryParameter("formatversion", "2")
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
83
app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.kt
Normal file
83
app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.kt
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
package fr.free.nrw.commons.mwapi
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import fr.free.nrw.commons.BuildConfig
|
||||||
|
import fr.free.nrw.commons.category.CATEGORY_PREFIX
|
||||||
|
import fr.free.nrw.commons.category.CategoryItem
|
||||||
|
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse
|
||||||
|
import io.reactivex.Single
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates
|
||||||
|
* with nearby Commons categories. Parses the results using GSON to obtain a list of relevant
|
||||||
|
* categories. Note: that caller is responsible for executing the request() method on a background
|
||||||
|
* thread.
|
||||||
|
*/
|
||||||
|
class CategoryApi @Inject constructor(
|
||||||
|
private val okHttpClient: OkHttpClient,
|
||||||
|
private val gson: Gson
|
||||||
|
) {
|
||||||
|
private val apiUrl : HttpUrl by lazy { BuildConfig.WIKIMEDIA_API_HOST.toHttpUrlOrNull()!! }
|
||||||
|
|
||||||
|
fun request(coords: String): Single<List<CategoryItem>> = Single.fromCallable {
|
||||||
|
val apiUrl = buildUrl(coords)
|
||||||
|
Timber.d("URL: %s", apiUrl.toString())
|
||||||
|
|
||||||
|
val request: Request = Request.Builder().get().url(apiUrl).build()
|
||||||
|
val response = okHttpClient.newCall(request).execute()
|
||||||
|
val body = response.body ?: return@fromCallable emptyList<CategoryItem>()
|
||||||
|
|
||||||
|
val apiResponse = gson.fromJson(body.charStream(), MwQueryResponse::class.java)
|
||||||
|
val categories: MutableSet<CategoryItem> = mutableSetOf()
|
||||||
|
if (apiResponse?.query() != null && apiResponse.query()!!.pages() != null) {
|
||||||
|
for (page in apiResponse.query()!!.pages()!!) {
|
||||||
|
if (page.categories() != null) {
|
||||||
|
for (category in page.categories()!!) {
|
||||||
|
categories.add(
|
||||||
|
CategoryItem(
|
||||||
|
name = category.title().replace(CATEGORY_PREFIX, ""),
|
||||||
|
description = "",
|
||||||
|
thumbnail = "",
|
||||||
|
isSelected = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayList<CategoryItem>(categories)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds URL with image coords for MediaWiki API calls
|
||||||
|
* Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2
|
||||||
|
*
|
||||||
|
* @param coords Coordinates to build query with
|
||||||
|
* @return URL for API query
|
||||||
|
*/
|
||||||
|
private fun buildUrl(coords: String): HttpUrl = apiUrl.newBuilder()
|
||||||
|
.addQueryParameter("action", "query")
|
||||||
|
.addQueryParameter("prop", "categories|coordinates|pageprops")
|
||||||
|
.addQueryParameter("format", "json")
|
||||||
|
.addQueryParameter("clshow", "!hidden")
|
||||||
|
.addQueryParameter("coprop", "type|name|dim|country|region|globe")
|
||||||
|
.addQueryParameter("codistancefrompoint", coords)
|
||||||
|
.addQueryParameter("generator", "geosearch")
|
||||||
|
.addQueryParameter("ggscoord", coords)
|
||||||
|
.addQueryParameter("ggsradius", "10000")
|
||||||
|
.addQueryParameter("ggslimit", "10")
|
||||||
|
.addQueryParameter("ggsnamespace", "6")
|
||||||
|
.addQueryParameter("ggsprop", "type|name|dim|country|region|globe")
|
||||||
|
.addQueryParameter("ggsprimary", "all")
|
||||||
|
.addQueryParameter("formatversion", "2")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,677 +0,0 @@
|
||||||
package fr.free.nrw.commons.mwapi;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.LEADERBOARD_END_POINT;
|
|
||||||
import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.UPDATE_AVATAR_END_POINT;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import fr.free.nrw.commons.campaigns.CampaignResponseDTO;
|
|
||||||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
|
||||||
import fr.free.nrw.commons.nearby.Place;
|
|
||||||
import fr.free.nrw.commons.nearby.model.ItemsClass;
|
|
||||||
import fr.free.nrw.commons.nearby.model.NearbyResponse;
|
|
||||||
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
|
|
||||||
import fr.free.nrw.commons.nearby.model.PlaceBindings;
|
|
||||||
import fr.free.nrw.commons.profile.achievements.FeaturedImages;
|
|
||||||
import fr.free.nrw.commons.profile.achievements.FeedbackResponse;
|
|
||||||
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse;
|
|
||||||
import fr.free.nrw.commons.profile.leaderboard.UpdateAvatarResponse;
|
|
||||||
import fr.free.nrw.commons.upload.FileUtils;
|
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
|
||||||
import fr.free.nrw.commons.wikidata.model.GetWikidataEditCountResponse;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.Single;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import okhttp3.HttpUrl;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import okhttp3.ResponseBody;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test methods in ok http api client
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class OkHttpJsonApiClient {
|
|
||||||
|
|
||||||
private final OkHttpClient okHttpClient;
|
|
||||||
private final DepictsClient depictsClient;
|
|
||||||
private final HttpUrl wikiMediaToolforgeUrl;
|
|
||||||
private final String sparqlQueryUrl;
|
|
||||||
private final String campaignsUrl;
|
|
||||||
private final Gson gson;
|
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public OkHttpJsonApiClient(OkHttpClient okHttpClient,
|
|
||||||
DepictsClient depictsClient,
|
|
||||||
HttpUrl wikiMediaToolforgeUrl,
|
|
||||||
String sparqlQueryUrl,
|
|
||||||
String campaignsUrl,
|
|
||||||
Gson gson) {
|
|
||||||
this.okHttpClient = okHttpClient;
|
|
||||||
this.depictsClient = depictsClient;
|
|
||||||
this.wikiMediaToolforgeUrl = wikiMediaToolforgeUrl;
|
|
||||||
this.sparqlQueryUrl = sparqlQueryUrl;
|
|
||||||
this.campaignsUrl = campaignsUrl;
|
|
||||||
this.gson = gson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method will gradually calls the leaderboard API and fetches the leaderboard
|
|
||||||
*
|
|
||||||
* @param userName username of leaderboard user
|
|
||||||
* @param duration duration for leaderboard
|
|
||||||
* @param category category for leaderboard
|
|
||||||
* @param limit page size limit for list
|
|
||||||
* @param offset offset for the list
|
|
||||||
* @return LeaderboardResponse object
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public Observable<LeaderboardResponse> getLeaderboard(String userName, String duration,
|
|
||||||
String category, String limit, String offset) {
|
|
||||||
final String fetchLeaderboardUrlTemplate = wikiMediaToolforgeUrl
|
|
||||||
+ LEADERBOARD_END_POINT;
|
|
||||||
String url = String.format(Locale.ENGLISH,
|
|
||||||
fetchLeaderboardUrlTemplate,
|
|
||||||
userName,
|
|
||||||
duration,
|
|
||||||
category,
|
|
||||||
limit,
|
|
||||||
offset);
|
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
|
||||||
urlBuilder.addQueryParameter("user", userName);
|
|
||||||
urlBuilder.addQueryParameter("duration", duration);
|
|
||||||
urlBuilder.addQueryParameter("category", category);
|
|
||||||
urlBuilder.addQueryParameter("limit", limit);
|
|
||||||
urlBuilder.addQueryParameter("offset", offset);
|
|
||||||
Timber.i("Url %s", urlBuilder.toString());
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.toString())
|
|
||||||
.build();
|
|
||||||
return Observable.fromCallable(() -> {
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response != null && response.body() != null && response.isSuccessful()) {
|
|
||||||
String json = response.body().string();
|
|
||||||
if (json == null) {
|
|
||||||
return new LeaderboardResponse();
|
|
||||||
}
|
|
||||||
Timber.d("Response for leaderboard is %s", json);
|
|
||||||
try {
|
|
||||||
return gson.fromJson(json, LeaderboardResponse.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return new LeaderboardResponse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new LeaderboardResponse();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will update the leaderboard user avatar
|
|
||||||
*
|
|
||||||
* @param username username to update
|
|
||||||
* @param avatar url of the new avatar
|
|
||||||
* @return UpdateAvatarResponse object
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public Single<UpdateAvatarResponse> setAvatar(String username, String avatar) {
|
|
||||||
final String urlTemplate = wikiMediaToolforgeUrl
|
|
||||||
+ UPDATE_AVATAR_END_POINT;
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
String url = String.format(Locale.ENGLISH,
|
|
||||||
urlTemplate,
|
|
||||||
username,
|
|
||||||
avatar);
|
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
|
||||||
urlBuilder.addQueryParameter("user", username);
|
|
||||||
urlBuilder.addQueryParameter("avatar", avatar);
|
|
||||||
Timber.i("Url %s", urlBuilder.toString());
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.toString())
|
|
||||||
.build();
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response != null && response.body() != null && response.isSuccessful()) {
|
|
||||||
String json = response.body().string();
|
|
||||||
if (json == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return gson.fromJson(json, UpdateAvatarResponse.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return new UpdateAvatarResponse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public Single<Integer> getUploadCount(String userName) {
|
|
||||||
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
|
|
||||||
urlBuilder
|
|
||||||
.addPathSegments("uploadsbyuser.py")
|
|
||||||
.addQueryParameter("user", userName);
|
|
||||||
|
|
||||||
if (ConfigUtils.isBetaFlavour()) {
|
|
||||||
urlBuilder.addQueryParameter("labs", "commonswiki");
|
|
||||||
}
|
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response != null && response.isSuccessful()) {
|
|
||||||
ResponseBody responseBody = response.body();
|
|
||||||
if (null != responseBody) {
|
|
||||||
String responseBodyString = responseBody.string().trim();
|
|
||||||
if (!TextUtils.isEmpty(responseBodyString)) {
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(responseBodyString);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public Single<Integer> getWikidataEdits(String userName) {
|
|
||||||
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
|
|
||||||
urlBuilder
|
|
||||||
.addPathSegments("wikidataedits.py")
|
|
||||||
.addQueryParameter("user", userName);
|
|
||||||
|
|
||||||
if (ConfigUtils.isBetaFlavour()) {
|
|
||||||
urlBuilder.addQueryParameter("labs", "commonswiki");
|
|
||||||
}
|
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response != null &&
|
|
||||||
response.isSuccessful() && response.body() != null) {
|
|
||||||
String json = response.body().string();
|
|
||||||
if (json == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Extract JSON from response
|
|
||||||
json = json.substring(json.indexOf('{'));
|
|
||||||
GetWikidataEditCountResponse countResponse = gson
|
|
||||||
.fromJson(json, GetWikidataEditCountResponse.class);
|
|
||||||
if (null != countResponse) {
|
|
||||||
return countResponse.getWikidataEditCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This takes userName as input, which is then used to fetch the feedback/achievements
|
|
||||||
* statistics using OkHttp and JavaRx. This function return JSONObject
|
|
||||||
*
|
|
||||||
* @param userName MediaWiki user name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Single<FeedbackResponse> getAchievements(String userName) {
|
|
||||||
final String fetchAchievementUrlTemplate =
|
|
||||||
wikiMediaToolforgeUrl + (ConfigUtils.isBetaFlavour() ? "/feedback.py?labs=commonswiki"
|
|
||||||
: "/feedback.py");
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
String url = String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
fetchAchievementUrlTemplate,
|
|
||||||
userName);
|
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
|
||||||
urlBuilder.addQueryParameter("user", userName);
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.toString())
|
|
||||||
.build();
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response != null && response.body() != null && response.isSuccessful()) {
|
|
||||||
String json = response.body().string();
|
|
||||||
if (json == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Extract JSON from response
|
|
||||||
json = json.substring(json.indexOf('{'));
|
|
||||||
Timber.d("Response for achievements is %s", json);
|
|
||||||
try {
|
|
||||||
return gson.fromJson(json, FeedbackResponse.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return new FeedbackResponse(0, 0, 0, new FeaturedImages(0, 0), 0, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make API Call to get Nearby Places
|
|
||||||
*
|
|
||||||
* @param cur Search lat long
|
|
||||||
* @param language Language
|
|
||||||
* @param radius Search Radius
|
|
||||||
* @return
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius,
|
|
||||||
final String customQuery)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
Timber.d("Fetching nearby items at radius %s", radius);
|
|
||||||
Timber.d("CUSTOM_SPARQL: %s", String.valueOf(customQuery != null));
|
|
||||||
final String wikidataQuery;
|
|
||||||
if (customQuery != null) {
|
|
||||||
wikidataQuery = customQuery;
|
|
||||||
} else {
|
|
||||||
wikidataQuery = FileUtils.readFromResource(
|
|
||||||
"/queries/radius_query_for_upload_wizard.rq");
|
|
||||||
}
|
|
||||||
final String query = wikidataQuery
|
|
||||||
.replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius))
|
|
||||||
.replace("${LAT}", String.format(Locale.ROOT, "%.4f", cur.getLatitude()))
|
|
||||||
.replace("${LONG}", String.format(Locale.ROOT, "%.4f", cur.getLongitude()))
|
|
||||||
.replace("${LANG}", language);
|
|
||||||
|
|
||||||
final HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(sparqlQueryUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("query", query)
|
|
||||||
.addQueryParameter("format", "json");
|
|
||||||
|
|
||||||
final Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
final Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response.body() != null && response.isSuccessful()) {
|
|
||||||
final String json = response.body().string();
|
|
||||||
final NearbyResponse nearbyResponse = gson.fromJson(json, NearbyResponse.class);
|
|
||||||
final List<NearbyResultItem> bindings = nearbyResponse.getResults().getBindings();
|
|
||||||
final List<Place> places = new ArrayList<>();
|
|
||||||
for (final NearbyResultItem item : bindings) {
|
|
||||||
final Place placeFromNearbyItem = Place.from(item);
|
|
||||||
placeFromNearbyItem.setMonument(false);
|
|
||||||
places.add(placeFromNearbyItem);
|
|
||||||
}
|
|
||||||
return places;
|
|
||||||
}
|
|
||||||
throw new Exception(response.message());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves nearby places based on screen coordinates and optional query parameters.
|
|
||||||
*
|
|
||||||
* @param screenTopRight The top right corner of the screen (latitude, longitude).
|
|
||||||
* @param screenBottomLeft The bottom left corner of the screen (latitude, longitude).
|
|
||||||
* @param language The language for the query.
|
|
||||||
* @param shouldQueryForMonuments Flag indicating whether to include monuments in the query.
|
|
||||||
* @param customQuery Optional custom SPARQL query to use instead of default
|
|
||||||
* queries.
|
|
||||||
* @return A list of nearby places.
|
|
||||||
* @throws Exception If an error occurs during the retrieval process.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public List<Place> getNearbyPlaces(
|
|
||||||
final fr.free.nrw.commons.location.LatLng screenTopRight,
|
|
||||||
final fr.free.nrw.commons.location.LatLng screenBottomLeft, final String language,
|
|
||||||
final boolean shouldQueryForMonuments, final String customQuery)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
Timber.d("CUSTOM_SPARQL: %s", String.valueOf(customQuery != null));
|
|
||||||
|
|
||||||
final String wikidataQuery;
|
|
||||||
if (customQuery != null) {
|
|
||||||
wikidataQuery = customQuery;
|
|
||||||
} else if (!shouldQueryForMonuments) {
|
|
||||||
wikidataQuery = FileUtils.readFromResource("/queries/rectangle_query_for_nearby.rq");
|
|
||||||
} else {
|
|
||||||
wikidataQuery = FileUtils.readFromResource(
|
|
||||||
"/queries/rectangle_query_for_nearby_monuments.rq");
|
|
||||||
}
|
|
||||||
|
|
||||||
final double westCornerLat = screenTopRight.getLatitude();
|
|
||||||
final double westCornerLong = screenTopRight.getLongitude();
|
|
||||||
final double eastCornerLat = screenBottomLeft.getLatitude();
|
|
||||||
final double eastCornerLong = screenBottomLeft.getLongitude();
|
|
||||||
|
|
||||||
final String query = wikidataQuery
|
|
||||||
.replace("${LAT_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLat))
|
|
||||||
.replace("${LONG_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLong))
|
|
||||||
.replace("${LAT_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLat))
|
|
||||||
.replace("${LONG_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLong))
|
|
||||||
.replace("${LANG}", language);
|
|
||||||
final HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(sparqlQueryUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("query", query)
|
|
||||||
.addQueryParameter("format", "json");
|
|
||||||
|
|
||||||
final Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
final Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response.body() != null && response.isSuccessful()) {
|
|
||||||
final String json = response.body().string();
|
|
||||||
final NearbyResponse nearbyResponse = gson.fromJson(json, NearbyResponse.class);
|
|
||||||
final List<NearbyResultItem> bindings = nearbyResponse.getResults().getBindings();
|
|
||||||
final List<Place> places = new ArrayList<>();
|
|
||||||
for (final NearbyResultItem item : bindings) {
|
|
||||||
final Place placeFromNearbyItem = Place.from(item);
|
|
||||||
if (shouldQueryForMonuments && item.getMonument() != null) {
|
|
||||||
placeFromNearbyItem.setMonument(true);
|
|
||||||
} else {
|
|
||||||
placeFromNearbyItem.setMonument(false);
|
|
||||||
}
|
|
||||||
places.add(placeFromNearbyItem);
|
|
||||||
}
|
|
||||||
return places;
|
|
||||||
}
|
|
||||||
throw new Exception(response.message());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a list of places based on the provided list of places and language.
|
|
||||||
*
|
|
||||||
* @param placeList A list of Place objects for which to fetch information.
|
|
||||||
* @param language The language code to use for the query.
|
|
||||||
* @return A list of Place objects with additional information retrieved from Wikidata, or null
|
|
||||||
* if an error occurs.
|
|
||||||
* @throws IOException If there is an issue with reading the resource file or executing the HTTP
|
|
||||||
* request.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public List<Place> getPlaces(
|
|
||||||
final List<Place> placeList, final String language) throws IOException {
|
|
||||||
final String wikidataQuery = FileUtils.readFromResource("/queries/query_for_item.rq");
|
|
||||||
String qids = "";
|
|
||||||
for (final Place place : placeList) {
|
|
||||||
qids += "\n" + ("wd:" + place.getWikiDataEntityId());
|
|
||||||
}
|
|
||||||
final String query = wikidataQuery
|
|
||||||
.replace("${ENTITY}", qids)
|
|
||||||
.replace("${LANG}", language);
|
|
||||||
final HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(sparqlQueryUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("query", query)
|
|
||||||
.addQueryParameter("format", "json");
|
|
||||||
|
|
||||||
final Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try (Response response = okHttpClient.newCall(request).execute()) {
|
|
||||||
if (response.isSuccessful()) {
|
|
||||||
final String json = response.body().string();
|
|
||||||
final NearbyResponse nearbyResponse = gson.fromJson(json, NearbyResponse.class);
|
|
||||||
final List<NearbyResultItem> bindings = nearbyResponse.getResults().getBindings();
|
|
||||||
final List<Place> places = new ArrayList<>();
|
|
||||||
for (final NearbyResultItem item : bindings) {
|
|
||||||
final Place placeFromNearbyItem = Place.from(item);
|
|
||||||
places.add(placeFromNearbyItem);
|
|
||||||
}
|
|
||||||
return places;
|
|
||||||
} else {
|
|
||||||
throw new IOException("Unexpected response code: " + response.code());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make API Call to get Places
|
|
||||||
*
|
|
||||||
* @param leftLatLng Left lat long
|
|
||||||
* @param rightLatLng Right lat long
|
|
||||||
* @return
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public String getPlacesAsKML(final LatLng leftLatLng, final LatLng rightLatLng)
|
|
||||||
throws Exception {
|
|
||||||
String kmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
|
||||||
"<!--Created by Wikimedia Commons Android app -->\n" +
|
|
||||||
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" +
|
|
||||||
" <Document>";
|
|
||||||
List<PlaceBindings> placeBindings = runQuery(leftLatLng,
|
|
||||||
rightLatLng);
|
|
||||||
if (placeBindings != null) {
|
|
||||||
for (PlaceBindings item : placeBindings) {
|
|
||||||
if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) {
|
|
||||||
String input = item.getLocation().getValue();
|
|
||||||
Pattern pattern = Pattern.compile(
|
|
||||||
"Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)");
|
|
||||||
Matcher matcher = pattern.matcher(input);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
String longStr = matcher.group(1);
|
|
||||||
String latStr = matcher.group(2);
|
|
||||||
String itemUrl = item.getItem().getValue();
|
|
||||||
String itemName = item.getLabel().getValue().replace("&", "&");
|
|
||||||
String itemLatitude = latStr;
|
|
||||||
String itemLongitude = longStr;
|
|
||||||
String itemClass = item.getClas().getValue();
|
|
||||||
|
|
||||||
String formattedItemName =
|
|
||||||
!itemClass.isEmpty() ? itemName + " (" + itemClass + ")"
|
|
||||||
: itemName;
|
|
||||||
|
|
||||||
String kmlEntry = "\n <Placemark>\n" +
|
|
||||||
" <name>" + formattedItemName + "</name>\n" +
|
|
||||||
" <description>" + itemUrl + "</description>\n" +
|
|
||||||
" <Point>\n" +
|
|
||||||
" <coordinates>" + itemLongitude + ","
|
|
||||||
+ itemLatitude
|
|
||||||
+ "</coordinates>\n" +
|
|
||||||
" </Point>\n" +
|
|
||||||
" </Placemark>";
|
|
||||||
kmlString = kmlString + kmlEntry;
|
|
||||||
} else {
|
|
||||||
Timber.e("No match found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kmlString = kmlString + "\n </Document>\n" +
|
|
||||||
"</kml>\n";
|
|
||||||
return kmlString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make API Call to get Places
|
|
||||||
*
|
|
||||||
* @param leftLatLng Left lat long
|
|
||||||
* @param rightLatLng Right lat long
|
|
||||||
* @return
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public String getPlacesAsGPX(final LatLng leftLatLng, final LatLng rightLatLng)
|
|
||||||
throws Exception {
|
|
||||||
String gpxString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
|
||||||
"<gpx\n" +
|
|
||||||
" version=\"1.0\"\n" +
|
|
||||||
" creator=\"Wikimedia Commons Android app\"\n" +
|
|
||||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
|
|
||||||
" xmlns=\"http://www.topografix.com/GPX/1/0\"\n" +
|
|
||||||
" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">"
|
|
||||||
+ "\n<bounds minlat=\"$MIN_LATITUDE\" minlon=\"$MIN_LONGITUDE\" maxlat=\"$MAX_LATITUDE\" maxlon=\"$MAX_LONGITUDE\"/>";
|
|
||||||
|
|
||||||
List<PlaceBindings> placeBindings = runQuery(leftLatLng, rightLatLng);
|
|
||||||
if (placeBindings != null) {
|
|
||||||
for (PlaceBindings item : placeBindings) {
|
|
||||||
if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) {
|
|
||||||
String input = item.getLocation().getValue();
|
|
||||||
Pattern pattern = Pattern.compile(
|
|
||||||
"Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)");
|
|
||||||
Matcher matcher = pattern.matcher(input);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
String longStr = matcher.group(1);
|
|
||||||
String latStr = matcher.group(2);
|
|
||||||
String itemUrl = item.getItem().getValue();
|
|
||||||
String itemName = item.getLabel().getValue().replace("&", "&");
|
|
||||||
String itemLatitude = latStr;
|
|
||||||
String itemLongitude = longStr;
|
|
||||||
String itemClass = item.getClas().getValue();
|
|
||||||
|
|
||||||
String formattedItemName =
|
|
||||||
!itemClass.isEmpty() ? itemName + " (" + itemClass + ")"
|
|
||||||
: itemName;
|
|
||||||
|
|
||||||
String gpxEntry =
|
|
||||||
"\n <wpt lat=\"" + itemLatitude + "\" lon=\"" + itemLongitude
|
|
||||||
+ "\">\n" +
|
|
||||||
" <name>" + itemName + "</name>\n" +
|
|
||||||
" <url>" + itemUrl + "</url>\n" +
|
|
||||||
" </wpt>";
|
|
||||||
gpxString = gpxString + gpxEntry;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Timber.e("No match found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
gpxString = gpxString + "\n</gpx>";
|
|
||||||
return gpxString;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<PlaceBindings> runQuery(final LatLng currentLatLng, final LatLng nextLatLng)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
final String wikidataQuery = FileUtils.readFromResource("/queries/places_query.rq");
|
|
||||||
final String query = wikidataQuery
|
|
||||||
.replace("${LONGITUDE}",
|
|
||||||
String.format(Locale.ROOT, "%.2f", currentLatLng.getLongitude()))
|
|
||||||
.replace("${LATITUDE}", String.format(Locale.ROOT, "%.4f", currentLatLng.getLatitude()))
|
|
||||||
.replace("${NEXT_LONGITUDE}",
|
|
||||||
String.format(Locale.ROOT, "%.4f", nextLatLng.getLongitude()))
|
|
||||||
.replace("${NEXT_LATITUDE}",
|
|
||||||
String.format(Locale.ROOT, "%.4f", nextLatLng.getLatitude()));
|
|
||||||
|
|
||||||
final HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(sparqlQueryUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("query", query)
|
|
||||||
.addQueryParameter("format", "json");
|
|
||||||
|
|
||||||
final Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
final Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response.body() != null && response.isSuccessful()) {
|
|
||||||
final String json = response.body().string();
|
|
||||||
final ItemsClass item = gson.fromJson(json, ItemsClass.class);
|
|
||||||
return item.getResults().getBindings();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make API Call to get Nearby Places Implementation does not expects a custom query
|
|
||||||
*
|
|
||||||
* @param cur Search lat long
|
|
||||||
* @param language Language
|
|
||||||
* @param radius Search Radius
|
|
||||||
* @return
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius)
|
|
||||||
throws Exception {
|
|
||||||
return getNearbyPlaces(cur, language, radius, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
|
|
||||||
* bridge -> suspended bridge, aqueduct, etc
|
|
||||||
*/
|
|
||||||
public Single<List<DepictedItem>> getChildDepictions(String qid, int startPosition,
|
|
||||||
int limit) throws IOException {
|
|
||||||
return depictedItemsFrom(
|
|
||||||
sparqlQuery(qid, startPosition, limit, "/queries/subclasses_query.rq"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
|
|
||||||
* bridge -> suspended bridge, aqueduct, etc
|
|
||||||
*/
|
|
||||||
public Single<List<DepictedItem>> getParentDepictions(String qid, int startPosition,
|
|
||||||
int limit) throws IOException {
|
|
||||||
return depictedItemsFrom(sparqlQuery(qid, startPosition, limit,
|
|
||||||
"/queries/parentclasses_query.rq"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Single<List<DepictedItem>> depictedItemsFrom(Request request) {
|
|
||||||
return depictsClient.toDepictions(Single.fromCallable(() -> {
|
|
||||||
try (ResponseBody body = okHttpClient.newCall(request).execute().body()) {
|
|
||||||
return gson.fromJson(body.string(), SparqlResponse.class);
|
|
||||||
}
|
|
||||||
}).doOnError(Timber::e));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Request sparqlQuery(String qid, int startPosition, int limit, String fileName)
|
|
||||||
throws IOException {
|
|
||||||
String query = FileUtils.readFromResource(fileName)
|
|
||||||
.replace("${QID}", qid)
|
|
||||||
.replace("${LANG}", "\"" + Locale.getDefault().getLanguage() + "\"")
|
|
||||||
.replace("${LIMIT}", "" + limit)
|
|
||||||
.replace("${OFFSET}", "" + startPosition);
|
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(sparqlQueryUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("query", query)
|
|
||||||
.addQueryParameter("format", "json");
|
|
||||||
return new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Single<CampaignResponseDTO> getCampaigns() {
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
Request request = new Request.Builder().url(campaignsUrl)
|
|
||||||
.build();
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response != null && response.body() != null && response.isSuccessful()) {
|
|
||||||
String json = response.body().string();
|
|
||||||
if (json == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return gson.fromJson(json, CampaignResponseDTO.class);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,543 @@
|
||||||
|
package fr.free.nrw.commons.mwapi
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import fr.free.nrw.commons.campaigns.CampaignResponseDTO
|
||||||
|
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||||
|
import fr.free.nrw.commons.location.LatLng
|
||||||
|
import fr.free.nrw.commons.nearby.Place
|
||||||
|
import fr.free.nrw.commons.nearby.model.ItemsClass
|
||||||
|
import fr.free.nrw.commons.nearby.model.NearbyResponse
|
||||||
|
import fr.free.nrw.commons.nearby.model.PlaceBindings
|
||||||
|
import fr.free.nrw.commons.profile.achievements.FeaturedImages
|
||||||
|
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
|
||||||
|
import fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants
|
||||||
|
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse
|
||||||
|
import fr.free.nrw.commons.profile.leaderboard.UpdateAvatarResponse
|
||||||
|
import fr.free.nrw.commons.upload.FileUtils
|
||||||
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||||
|
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||||
|
import fr.free.nrw.commons.wikidata.model.GetWikidataEditCountResponse
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.Single
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test methods in ok http api client
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class OkHttpJsonApiClient @Inject constructor(
|
||||||
|
private val okHttpClient: OkHttpClient,
|
||||||
|
private val depictsClient: DepictsClient,
|
||||||
|
private val wikiMediaToolforgeUrl: HttpUrl,
|
||||||
|
private val sparqlQueryUrl: String,
|
||||||
|
private val campaignsUrl: String,
|
||||||
|
private val gson: Gson
|
||||||
|
) {
|
||||||
|
fun getLeaderboard(
|
||||||
|
userName: String, duration: String?,
|
||||||
|
category: String?, limit: String?, offset: String?
|
||||||
|
): Observable<LeaderboardResponse> {
|
||||||
|
val fetchLeaderboardUrlTemplate =
|
||||||
|
wikiMediaToolforgeUrl.toString() + LeaderboardConstants.LEADERBOARD_END_POINT
|
||||||
|
val url = String.format(Locale.ENGLISH,
|
||||||
|
fetchLeaderboardUrlTemplate, userName, duration, category, limit, offset)
|
||||||
|
val urlBuilder: HttpUrl.Builder = url.toHttpUrlOrNull()!!.newBuilder()
|
||||||
|
.addQueryParameter("user", userName)
|
||||||
|
.addQueryParameter("duration", duration)
|
||||||
|
.addQueryParameter("category", category)
|
||||||
|
.addQueryParameter("limit", limit)
|
||||||
|
.addQueryParameter("offset", offset)
|
||||||
|
Timber.i("Url %s", urlBuilder.toString())
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.toString())
|
||||||
|
.build()
|
||||||
|
return Observable.fromCallable({
|
||||||
|
val response: Response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
val json: String = response.body!!.string()
|
||||||
|
Timber.d("Response for leaderboard is %s", json)
|
||||||
|
try {
|
||||||
|
return@fromCallable gson.fromJson<LeaderboardResponse>(
|
||||||
|
json,
|
||||||
|
LeaderboardResponse::class.java
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return@fromCallable LeaderboardResponse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LeaderboardResponse()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAvatar(username: String, avatar: String?): Single<UpdateAvatarResponse?> {
|
||||||
|
val urlTemplate = wikiMediaToolforgeUrl
|
||||||
|
.toString() + LeaderboardConstants.UPDATE_AVATAR_END_POINT
|
||||||
|
return Single.fromCallable<UpdateAvatarResponse?>({
|
||||||
|
val url = String.format(Locale.ENGLISH, urlTemplate, username, avatar)
|
||||||
|
val urlBuilder: HttpUrl.Builder = url.toHttpUrlOrNull()!!.newBuilder()
|
||||||
|
.addQueryParameter("user", username)
|
||||||
|
.addQueryParameter("avatar", avatar)
|
||||||
|
Timber.i("Url %s", urlBuilder.toString())
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.toString())
|
||||||
|
.build()
|
||||||
|
val response: Response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
val json: String = response.body!!.string() ?: return@fromCallable null
|
||||||
|
try {
|
||||||
|
return@fromCallable gson.fromJson<UpdateAvatarResponse>(
|
||||||
|
json,
|
||||||
|
UpdateAvatarResponse::class.java
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return@fromCallable UpdateAvatarResponse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUploadCount(userName: String?): Single<Int> {
|
||||||
|
val urlBuilder: HttpUrl.Builder = wikiMediaToolforgeUrl.newBuilder()
|
||||||
|
.addPathSegments("uploadsbyuser.py")
|
||||||
|
.addQueryParameter("user", userName)
|
||||||
|
|
||||||
|
if (isBetaFlavour) {
|
||||||
|
urlBuilder.addQueryParameter("labs", "commonswiki")
|
||||||
|
}
|
||||||
|
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return Single.fromCallable<Int>({
|
||||||
|
val response: Response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response != null && response.isSuccessful) {
|
||||||
|
val responseBody = response.body
|
||||||
|
if (null != responseBody) {
|
||||||
|
val responseBodyString = responseBody.string().trim { it <= ' ' }
|
||||||
|
if (!TextUtils.isEmpty(responseBodyString)) {
|
||||||
|
try {
|
||||||
|
return@fromCallable responseBodyString.toInt()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWikidataEdits(userName: String?): Single<Int> {
|
||||||
|
val urlBuilder: HttpUrl.Builder = wikiMediaToolforgeUrl.newBuilder()
|
||||||
|
.addPathSegments("wikidataedits.py")
|
||||||
|
.addQueryParameter("user", userName)
|
||||||
|
|
||||||
|
if (isBetaFlavour) {
|
||||||
|
urlBuilder.addQueryParameter("labs", "commonswiki")
|
||||||
|
}
|
||||||
|
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return Single.fromCallable<Int>({
|
||||||
|
val response: Response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response != null && response.isSuccessful && response.body != null) {
|
||||||
|
var json: String = response.body!!.string()
|
||||||
|
// Extract JSON from response
|
||||||
|
json = json.substring(json.indexOf('{'))
|
||||||
|
val countResponse = gson
|
||||||
|
.fromJson(
|
||||||
|
json,
|
||||||
|
GetWikidataEditCountResponse::class.java
|
||||||
|
)
|
||||||
|
if (null != countResponse) {
|
||||||
|
return@fromCallable countResponse.wikidataEditCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAchievements(userName: String?): Single<FeedbackResponse?> {
|
||||||
|
val suffix = if (isBetaFlavour) "/feedback.py?labs=commonswiki" else "/feedback.py"
|
||||||
|
val fetchAchievementUrlTemplate = wikiMediaToolforgeUrl.toString() + suffix
|
||||||
|
return Single.fromCallable<FeedbackResponse?>({
|
||||||
|
val url = String.format(
|
||||||
|
Locale.ENGLISH,
|
||||||
|
fetchAchievementUrlTemplate,
|
||||||
|
userName
|
||||||
|
)
|
||||||
|
val urlBuilder: HttpUrl.Builder = url.toHttpUrlOrNull()!!.newBuilder()
|
||||||
|
.addQueryParameter("user", userName)
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.toString())
|
||||||
|
.build()
|
||||||
|
val response: Response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
var json: String = response.body!!.string()
|
||||||
|
// Extract JSON from response
|
||||||
|
json = json.substring(json.indexOf('{'))
|
||||||
|
Timber.d("Response for achievements is %s", json)
|
||||||
|
try {
|
||||||
|
return@fromCallable gson.fromJson<FeedbackResponse>(
|
||||||
|
json,
|
||||||
|
FeedbackResponse::class.java
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return@fromCallable FeedbackResponse(0, 0, 0, FeaturedImages(0, 0), 0, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getNearbyPlaces(
|
||||||
|
cur: LatLng, language: String, radius: Double,
|
||||||
|
customQuery: String? = null
|
||||||
|
): List<Place>? {
|
||||||
|
Timber.d("Fetching nearby items at radius %s", radius)
|
||||||
|
Timber.d("CUSTOM_SPARQL: %s", (customQuery != null).toString())
|
||||||
|
val wikidataQuery: String = if (customQuery != null) {
|
||||||
|
customQuery
|
||||||
|
} else {
|
||||||
|
FileUtils.readFromResource("/queries/radius_query_for_upload_wizard.rq")
|
||||||
|
}
|
||||||
|
val query = wikidataQuery
|
||||||
|
.replace("\${RAD}", String.format(Locale.ROOT, "%.2f", radius))
|
||||||
|
.replace("\${LAT}", String.format(Locale.ROOT, "%.4f", cur.latitude))
|
||||||
|
.replace("\${LONG}", String.format(Locale.ROOT, "%.4f", cur.longitude))
|
||||||
|
.replace("\${LANG}", language)
|
||||||
|
|
||||||
|
val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!!
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter("query", query)
|
||||||
|
.addQueryParameter("format", "json")
|
||||||
|
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
val json = response.body!!.string()
|
||||||
|
val nearbyResponse = gson.fromJson(json, NearbyResponse::class.java)
|
||||||
|
val bindings = nearbyResponse.results.bindings
|
||||||
|
val places: MutableList<Place> = ArrayList()
|
||||||
|
for (item in bindings) {
|
||||||
|
val placeFromNearbyItem = Place.from(item)
|
||||||
|
placeFromNearbyItem.isMonument = false
|
||||||
|
places.add(placeFromNearbyItem)
|
||||||
|
}
|
||||||
|
return places
|
||||||
|
}
|
||||||
|
throw Exception(response.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getNearbyPlaces(
|
||||||
|
screenTopRight: LatLng,
|
||||||
|
screenBottomLeft: LatLng, language: String,
|
||||||
|
shouldQueryForMonuments: Boolean, customQuery: String?
|
||||||
|
): List<Place>? {
|
||||||
|
Timber.d("CUSTOM_SPARQL: %s", (customQuery != null).toString())
|
||||||
|
|
||||||
|
val wikidataQuery: String = if (customQuery != null) {
|
||||||
|
customQuery
|
||||||
|
} else if (!shouldQueryForMonuments) {
|
||||||
|
FileUtils.readFromResource("/queries/rectangle_query_for_nearby.rq")
|
||||||
|
} else {
|
||||||
|
FileUtils.readFromResource("/queries/rectangle_query_for_nearby_monuments.rq")
|
||||||
|
}
|
||||||
|
|
||||||
|
val westCornerLat = screenTopRight.latitude
|
||||||
|
val westCornerLong = screenTopRight.longitude
|
||||||
|
val eastCornerLat = screenBottomLeft.latitude
|
||||||
|
val eastCornerLong = screenBottomLeft.longitude
|
||||||
|
|
||||||
|
val query = wikidataQuery
|
||||||
|
.replace("\${LAT_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLat))
|
||||||
|
.replace("\${LONG_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLong))
|
||||||
|
.replace("\${LAT_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLat))
|
||||||
|
.replace("\${LONG_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLong))
|
||||||
|
.replace("\${LANG}", language)
|
||||||
|
val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!!
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter("query", query)
|
||||||
|
.addQueryParameter("format", "json")
|
||||||
|
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(urlBuilder.build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
val json = response.body!!.string()
|
||||||
|
val nearbyResponse = gson.fromJson(json, NearbyResponse::class.java)
|
||||||
|
val bindings = nearbyResponse.results.bindings
|
||||||
|
val places: MutableList<Place> = ArrayList()
|
||||||
|
for (item in bindings) {
|
||||||
|
val placeFromNearbyItem = Place.from(item)
|
||||||
|
if (shouldQueryForMonuments && item.getMonument() != null) {
|
||||||
|
placeFromNearbyItem.isMonument = true
|
||||||
|
} else {
|
||||||
|
placeFromNearbyItem.isMonument = false
|
||||||
|
}
|
||||||
|
places.add(placeFromNearbyItem)
|
||||||
|
}
|
||||||
|
return places
|
||||||
|
}
|
||||||
|
throw Exception(response.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getPlaces(
|
||||||
|
placeList: List<Place>, language: String
|
||||||
|
): List<Place>? {
|
||||||
|
val wikidataQuery = FileUtils.readFromResource("/queries/query_for_item.rq")
|
||||||
|
var qids = ""
|
||||||
|
for (place in placeList) {
|
||||||
|
qids += """
|
||||||
|
${"wd:" + place.wikiDataEntityId}"""
|
||||||
|
}
|
||||||
|
val query = wikidataQuery
|
||||||
|
.replace("\${ENTITY}", qids)
|
||||||
|
.replace("\${LANG}", language)
|
||||||
|
val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!!
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter("query", query)
|
||||||
|
.addQueryParameter("format", "json")
|
||||||
|
|
||||||
|
val request: Request = Request.Builder().url(urlBuilder.build()).build()
|
||||||
|
|
||||||
|
okHttpClient.newCall(request).execute().use { response ->
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val json = response.body!!.string()
|
||||||
|
val nearbyResponse = gson.fromJson(json, NearbyResponse::class.java)
|
||||||
|
val bindings = nearbyResponse.results.bindings
|
||||||
|
val places: MutableList<Place> = ArrayList()
|
||||||
|
for (item in bindings) {
|
||||||
|
val placeFromNearbyItem = Place.from(item)
|
||||||
|
places.add(placeFromNearbyItem)
|
||||||
|
}
|
||||||
|
return places
|
||||||
|
} else {
|
||||||
|
throw IOException("Unexpected response code: " + response.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getPlacesAsKML(leftLatLng: LatLng, rightLatLng: LatLng): String? {
|
||||||
|
var kmlString = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--Created by Wikimedia Commons Android app -->
|
||||||
|
<kml xmlns="http://www.opengis.net/kml/2.2">
|
||||||
|
<Document>"""
|
||||||
|
val placeBindings = runQuery(
|
||||||
|
leftLatLng,
|
||||||
|
rightLatLng
|
||||||
|
)
|
||||||
|
if (placeBindings != null) {
|
||||||
|
for ((item1, label, location, clas) in placeBindings) {
|
||||||
|
if (item1 != null && label != null && clas != null) {
|
||||||
|
val input = location.value
|
||||||
|
val pattern = Pattern.compile(
|
||||||
|
"Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)"
|
||||||
|
)
|
||||||
|
val matcher = pattern.matcher(input)
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
val longStr = matcher.group(1)
|
||||||
|
val latStr = matcher.group(2)
|
||||||
|
val itemUrl = item1.value
|
||||||
|
val itemName = label.value.replace("&", "&")
|
||||||
|
val itemLatitude = latStr
|
||||||
|
val itemLongitude = longStr
|
||||||
|
val itemClass = clas.value
|
||||||
|
|
||||||
|
val formattedItemName =
|
||||||
|
if (!itemClass.isEmpty())
|
||||||
|
"$itemName ($itemClass)"
|
||||||
|
else
|
||||||
|
itemName
|
||||||
|
|
||||||
|
val kmlEntry = ("""
|
||||||
|
<Placemark>
|
||||||
|
<name>$formattedItemName</name>
|
||||||
|
<description>$itemUrl</description>
|
||||||
|
<Point>
|
||||||
|
<coordinates>$itemLongitude,$itemLatitude</coordinates>
|
||||||
|
</Point>
|
||||||
|
</Placemark>""")
|
||||||
|
kmlString = kmlString + kmlEntry
|
||||||
|
} else {
|
||||||
|
Timber.e("No match found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kmlString = """$kmlString
|
||||||
|
</Document>
|
||||||
|
</kml>
|
||||||
|
"""
|
||||||
|
return kmlString
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getPlacesAsGPX(leftLatLng: LatLng, rightLatLng: LatLng): String? {
|
||||||
|
var gpxString = ("""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<gpx
|
||||||
|
version="1.0"
|
||||||
|
creator="Wikimedia Commons Android app"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.topografix.com/GPX/1/0"
|
||||||
|
xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
|
||||||
|
<bounds minlat="${"$"}MIN_LATITUDE" minlon="${"$"}MIN_LONGITUDE" maxlat="${"$"}MAX_LATITUDE" maxlon="${"$"}MAX_LONGITUDE"/>""")
|
||||||
|
|
||||||
|
val placeBindings = runQuery(leftLatLng, rightLatLng)
|
||||||
|
if (placeBindings != null) {
|
||||||
|
for ((item1, label, location, clas) in placeBindings) {
|
||||||
|
if (item1 != null && label != null && clas != null) {
|
||||||
|
val input = location.value
|
||||||
|
val pattern = Pattern.compile(
|
||||||
|
"Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)"
|
||||||
|
)
|
||||||
|
val matcher = pattern.matcher(input)
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
val longStr = matcher.group(1)
|
||||||
|
val latStr = matcher.group(2)
|
||||||
|
val itemUrl = item1.value
|
||||||
|
val itemName = label.value.replace("&", "&")
|
||||||
|
val itemLatitude = latStr
|
||||||
|
val itemLongitude = longStr
|
||||||
|
val itemClass = clas.value
|
||||||
|
|
||||||
|
val formattedItemName = if (!itemClass.isEmpty())
|
||||||
|
"$itemName ($itemClass)"
|
||||||
|
else
|
||||||
|
itemName
|
||||||
|
|
||||||
|
val gpxEntry =
|
||||||
|
("""
|
||||||
|
<wpt lat="$itemLatitude" lon="$itemLongitude">
|
||||||
|
<name>$itemName</name>
|
||||||
|
<url>$itemUrl</url>
|
||||||
|
</wpt>""")
|
||||||
|
gpxString = gpxString + gpxEntry
|
||||||
|
} else {
|
||||||
|
Timber.e("No match found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gpxString = "$gpxString\n</gpx>"
|
||||||
|
return gpxString
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getChildDepictions(
|
||||||
|
qid: String, startPosition: Int,
|
||||||
|
limit: Int
|
||||||
|
): Single<List<DepictedItem>> =
|
||||||
|
depictedItemsFrom(sparqlQuery(qid, startPosition, limit, "/queries/subclasses_query.rq"))
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getParentDepictions(
|
||||||
|
qid: String, startPosition: Int,
|
||||||
|
limit: Int
|
||||||
|
): Single<List<DepictedItem>> = depictedItemsFrom(
|
||||||
|
sparqlQuery(
|
||||||
|
qid,
|
||||||
|
startPosition,
|
||||||
|
limit,
|
||||||
|
"/queries/parentclasses_query.rq"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getCampaigns(): Single<CampaignResponseDTO> {
|
||||||
|
return Single.fromCallable<CampaignResponseDTO?>({
|
||||||
|
val request: Request = Request.Builder().url(campaignsUrl).build()
|
||||||
|
val response: Response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
val json: String = response.body!!.string()
|
||||||
|
return@fromCallable gson.fromJson<CampaignResponseDTO>(
|
||||||
|
json,
|
||||||
|
CampaignResponseDTO::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun depictedItemsFrom(request: Request): Single<List<DepictedItem>> {
|
||||||
|
return depictsClient.toDepictions(Single.fromCallable({
|
||||||
|
okHttpClient.newCall(request).execute().body.use { body ->
|
||||||
|
return@fromCallable gson.fromJson<SparqlResponse>(
|
||||||
|
body!!.string(),
|
||||||
|
SparqlResponse::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).doOnError({ t: Throwable? -> Timber.e(t) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun sparqlQuery(
|
||||||
|
qid: String,
|
||||||
|
startPosition: Int,
|
||||||
|
limit: Int,
|
||||||
|
fileName: String
|
||||||
|
): Request {
|
||||||
|
val query = FileUtils.readFromResource(fileName)
|
||||||
|
.replace("\${QID}", qid)
|
||||||
|
.replace("\${LANG}", "\"" + Locale.getDefault().language + "\"")
|
||||||
|
.replace("\${LIMIT}", "" + limit)
|
||||||
|
.replace("\${OFFSET}", "" + startPosition)
|
||||||
|
val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!!
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter("query", query)
|
||||||
|
.addQueryParameter("format", "json")
|
||||||
|
return Request.Builder().url(urlBuilder.build()).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun runQuery(currentLatLng: LatLng, nextLatLng: LatLng): List<PlaceBindings>? {
|
||||||
|
val wikidataQuery = FileUtils.readFromResource("/queries/places_query.rq")
|
||||||
|
val query = wikidataQuery
|
||||||
|
.replace("\${LONGITUDE}", String.format(Locale.ROOT, "%.2f", currentLatLng.longitude))
|
||||||
|
.replace("\${LATITUDE}", String.format(Locale.ROOT, "%.4f", currentLatLng.latitude))
|
||||||
|
.replace("\${NEXT_LONGITUDE}", String.format(Locale.ROOT, "%.4f", nextLatLng.longitude))
|
||||||
|
.replace("\${NEXT_LATITUDE}", String.format(Locale.ROOT, "%.4f", nextLatLng.latitude))
|
||||||
|
|
||||||
|
val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!!
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter("query", query)
|
||||||
|
.addQueryParameter("format", "json")
|
||||||
|
|
||||||
|
val request: Request = Request.Builder().url(urlBuilder.build()).build()
|
||||||
|
|
||||||
|
val response = okHttpClient.newCall(request).execute()
|
||||||
|
if (response.body != null && response.isSuccessful) {
|
||||||
|
val json = response.body!!.string()
|
||||||
|
val item = gson.fromJson(json, ItemsClass::class.java)
|
||||||
|
return item.results.bindings
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -194,7 +194,7 @@ class FileProcessor
|
||||||
requireNotNull(imageCoordinates.decimalCoords)
|
requireNotNull(imageCoordinates.decimalCoords)
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
apiCall
|
apiCall
|
||||||
.request(imageCoordinates.decimalCoords)
|
.request(imageCoordinates.decimalCoords!!)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
|
@ -220,7 +220,7 @@ class FileProcessor
|
||||||
.concatMap {
|
.concatMap {
|
||||||
Observable.fromCallable {
|
Observable.fromCallable {
|
||||||
okHttpJsonApiClient.getNearbyPlaces(
|
okHttpJsonApiClient.getNearbyPlaces(
|
||||||
imageCoordinates.latLng,
|
imageCoordinates.latLng!!,
|
||||||
Locale.getDefault().language,
|
Locale.getDefault().language,
|
||||||
it,
|
it,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -49,13 +49,13 @@ class CampaignsPresenterTest {
|
||||||
campaignsSingle = Single.just(campaignResponseDTO)
|
campaignsSingle = Single.just(campaignResponseDTO)
|
||||||
campaignsPresenter = CampaignsPresenter(okHttpJsonApiClient, testScheduler, testScheduler)
|
campaignsPresenter = CampaignsPresenter(okHttpJsonApiClient, testScheduler, testScheduler)
|
||||||
campaignsPresenter.onAttachView(view)
|
campaignsPresenter.onAttachView(view)
|
||||||
Mockito.`when`(okHttpJsonApiClient.campaigns).thenReturn(campaignsSingle)
|
Mockito.`when`(okHttpJsonApiClient.getCampaigns()).thenReturn(campaignsSingle)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getCampaignsTestNoCampaigns() {
|
fun getCampaignsTestNoCampaigns() {
|
||||||
campaignsPresenter.getCampaigns()
|
campaignsPresenter.getCampaigns()
|
||||||
verify(okHttpJsonApiClient).campaigns
|
verify(okHttpJsonApiClient).getCampaigns()
|
||||||
testScheduler.triggerActions()
|
testScheduler.triggerActions()
|
||||||
verify(view).showCampaigns(null)
|
verify(view).showCampaigns(null)
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +77,7 @@ class CampaignsPresenterTest {
|
||||||
Mockito.`when`(campaign.endDate).thenReturn(endDateString)
|
Mockito.`when`(campaign.endDate).thenReturn(endDateString)
|
||||||
Mockito.`when`(campaign.startDate).thenReturn(startDateString)
|
Mockito.`when`(campaign.startDate).thenReturn(startDateString)
|
||||||
Mockito.`when`(campaignResponseDTO.campaigns).thenReturn(campaigns)
|
Mockito.`when`(campaignResponseDTO.campaigns).thenReturn(campaigns)
|
||||||
verify(okHttpJsonApiClient).campaigns
|
verify(okHttpJsonApiClient).getCampaigns()
|
||||||
testScheduler.triggerActions()
|
testScheduler.triggerActions()
|
||||||
verify(view).showCampaigns(campaign)
|
verify(view).showCampaigns(campaign)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue