Skip to content

Commit 623c2f5

Browse files
ilgazermaskaravivek
authored andcommitted
getSubCategoryList and getParentCategoryList migrated to retrofit (commons-app#3055)
* Migrated getSubCategoryList to retrofit * Migrated getParentCategoryList to retrofit * Removed obsolete functions * Added tests * Fixed small bugs
1 parent 9712229 commit 623c2f5

File tree

8 files changed

+128
-143
lines changed

8 files changed

+128
-143
lines changed

app/src/main/java/fr/free/nrw/commons/category/CategoryClient.java

+33-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package fr.free.nrw.commons.category;
22

33

4+
import androidx.annotation.NonNull;
5+
46
import org.wikipedia.dataclient.mwapi.MwQueryPage;
57
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
8+
import org.wikipedia.dataclient.mwapi.MwQueryResult;
69

710
import java.util.List;
811

@@ -74,6 +77,29 @@ public Observable<String> searchCategoriesForPrefix(String prefix, int itemLimit
7477
}
7578

7679

80+
/**
81+
* The method takes categoryName as input and returns a List of Subcategories
82+
* It uses the generator query API to get the subcategories in a category, 500 at a time.
83+
*
84+
* @param categoryName Category name as defined on commons
85+
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
86+
*/
87+
public Observable<String> getSubCategoryList(String categoryName) {
88+
return responseToCategoryName(CategoryInterface.getSubCategoryList(categoryName));
89+
}
90+
91+
/**
92+
* The method takes categoryName as input and returns a List of parent categories
93+
* It uses the generator query API to get the parent categories of a category, 500 at a time.
94+
*
95+
* @param categoryName Category name as defined on commons
96+
* @return
97+
*/
98+
@NonNull
99+
public Observable<String> getParentCategoryList(String categoryName) {
100+
return responseToCategoryName(CategoryInterface.getParentCategoryList(categoryName));
101+
}
102+
77103
/**
78104
* Internal function to reduce code reuse. Extracts the categories returned from MwQueryResponse.
79105
*
@@ -83,12 +109,14 @@ public Observable<String> searchCategoriesForPrefix(String prefix, int itemLimit
83109
private Observable<String> responseToCategoryName(Observable<MwQueryResponse> responseObservable) {
84110
return responseObservable
85111
.flatMap(mwQueryResponse -> {
86-
List<MwQueryPage> pages = mwQueryResponse.query().pages();
87-
if (pages != null)
88-
return Observable.fromIterable(pages);
89-
else
112+
MwQueryResult query;
113+
List<MwQueryPage> pages;
114+
if ((query = mwQueryResponse.query()) == null ||
115+
(pages = query.pages()) == null) {
90116
Timber.d("No categories returned.");
91-
return Observable.empty();
117+
return Observable.empty();
118+
} else
119+
return Observable.fromIterable(pages);
92120
})
93121
.map(MwQueryPage::title)
94122
.doOnEach(s -> Timber.d("Category returned: %s", s))

app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public interface CategoryInterface {
1313

1414
/**
1515
* Searches for categories with the specified name.
16-
* Replaces ApacheHttpClientMediaWikiApi#allCategories
1716
*
1817
* @param filter The string to be searched
1918
* @param itemLimit How many results are returned
@@ -24,8 +23,25 @@ public interface CategoryInterface {
2423
Observable<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
2524
@Query("gsrlimit") int itemLimit, @Query("gsroffset") int offset);
2625

26+
/**
27+
* Searches for categories starting with the specified prefix.
28+
*
29+
* @param prefix The string to be searched
30+
* @param itemLimit How many results are returned
31+
* @return
32+
*/
2733
@GET("w/api.php?action=query&format=json&formatversion=2"
2834
+ "&generator=allcategories")
2935
Observable<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
3036
@Query("gaclimit") int itemLimit, @Query("gacoffset") int offset);
37+
38+
@GET("w/api.php?action=query&format=json&formatversion=2"
39+
+ "&generator=categorymembers&gcmtype=subcat"
40+
+ "&prop=info&gcmlimit=500")
41+
Observable<MwQueryResponse> getSubCategoryList(@Query("gcmtitle") String categoryName);
42+
43+
@GET("w/api.php?action=query&format=json&formatversion=2"
44+
+ "&generator=categories&prop=info&gcllimit=500")
45+
Observable<MwQueryResponse> getParentCategoryList(@Query("titles") String categoryName);
46+
3147
}

app/src/main/java/fr/free/nrw/commons/category/SubCategoryListFragment.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment {
5353
TextView categoriesNotFoundView;
5454

5555
private String categoryName = null;
56-
@Inject MediaWikiApi mwApi;
56+
@Inject CategoryClient categoryClient;
5757

5858
private RVRendererAdapter<String> categoriesAdapter;
5959
private boolean isParentCategory = true;
@@ -86,7 +86,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle sav
8686
}
8787

8888
/**
89-
* Checks for internet connection and then initializes the recycler view with 25 categories of the searched query
89+
* Checks for internet connection and then initializes the recycler view with all(max 500) categories of the searched query
9090
* Clearing categoryAdapter every time new keyword is searched so that user can see only new results
9191
*/
9292
public void initSubCategoryList() {
@@ -96,17 +96,19 @@ public void initSubCategoryList() {
9696
return;
9797
}
9898
progressBar.setVisibility(View.VISIBLE);
99-
if (!isParentCategory){
100-
compositeDisposable.add(Observable.fromCallable(() -> mwApi.getSubCategoryList(categoryName))
99+
if (isParentCategory) {
100+
compositeDisposable.add(categoryClient.getParentCategoryList("Category:"+categoryName)
101101
.subscribeOn(Schedulers.io())
102102
.observeOn(AndroidSchedulers.mainThread())
103103
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
104+
.collect(ArrayList<String>::new, ArrayList::add)
104105
.subscribe(this::handleSuccess, this::handleError));
105-
}else {
106-
compositeDisposable.add(Observable.fromCallable(() -> mwApi.getParentCategoryList(categoryName))
106+
} else {
107+
compositeDisposable.add(categoryClient.getSubCategoryList("Category:"+categoryName)
107108
.subscribeOn(Schedulers.io())
108109
.observeOn(AndroidSchedulers.mainThread())
109110
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
111+
.collect(ArrayList<String>::new, ArrayList::add)
110112
.subscribe(this::handleSuccess, this::handleError));
111113
}
112114
}

app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public HttpLoggingInterceptor provideHttpLoggingInterceptor() {
7171
public MediaWikiApi provideMediaWikiApi(Context context,
7272
@Named("default_preferences") JsonKvStore defaultKvStore,
7373
Gson gson) {
74-
return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultKvStore, gson);
74+
return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultKvStore);
7575
}
7676

7777
@Provides

app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java

+1-116
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,19 @@
2929
import java.io.InputStream;
3030
import java.text.ParseException;
3131
import java.util.ArrayList;
32-
import java.util.Collections;
3332
import java.util.Date;
3433
import java.util.List;
3534
import java.util.Locale;
36-
import java.util.concurrent.Callable;
3735

3836
import fr.free.nrw.commons.BuildConfig;
3937
import fr.free.nrw.commons.CommonsApplication;
4038
import fr.free.nrw.commons.R;
4139
import fr.free.nrw.commons.auth.AccountUtil;
42-
import fr.free.nrw.commons.category.CategoryImageUtils;
4340
import fr.free.nrw.commons.category.QueryContinue;
4441
import fr.free.nrw.commons.kvstore.JsonKvStore;
4542
import fr.free.nrw.commons.notification.Notification;
4643
import fr.free.nrw.commons.notification.NotificationUtils;
4744
import fr.free.nrw.commons.utils.ViewUtil;
48-
import io.reactivex.Observable;
4945
import io.reactivex.Single;
5046
import timber.log.Timber;
5147

@@ -58,15 +54,13 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
5854
private CustomMwApi wikidataApi;
5955
private Context context;
6056
private JsonKvStore defaultKvStore;
61-
private Gson gson;
6257

6358
private final String ERROR_CODE_BAD_TOKEN = "badtoken";
6459

6560
public ApacheHttpClientMediaWikiApi(Context context,
6661
String apiURL,
6762
String wikidatApiURL,
68-
JsonKvStore defaultKvStore,
69-
Gson gson) {
63+
JsonKvStore defaultKvStore) {
7064
this.context = context;
7165
BasicHttpParams params = new BasicHttpParams();
7266
SchemeRegistry schemeRegistry = new SchemeRegistry();
@@ -82,7 +76,6 @@ public ApacheHttpClientMediaWikiApi(Context context,
8276
api = new CustomMwApi(apiURL, httpClient);
8377
wikidataApi = new CustomMwApi(wikidatApiURL, httpClient);
8478
this.defaultKvStore = defaultKvStore;
85-
this.gson = gson;
8679
}
8780

8881
/**
@@ -491,114 +484,6 @@ public boolean markNotificationAsRead(Notification notification) throws IOExcept
491484
return result.equals("success");
492485
}
493486

494-
/**
495-
* The method takes categoryName as input and returns a List of Subcategories
496-
* It uses the generator query API to get the subcategories in a category, 500 at a time.
497-
* Uses the query continue values for fetching paginated responses
498-
*
499-
* @param categoryName Category name as defined on commons
500-
* @return
501-
*/
502-
@Override
503-
@NonNull
504-
public List<String> getSubCategoryList(String categoryName) {
505-
CustomApiResult apiResult = null;
506-
try {
507-
CustomMwApi.RequestBuilder requestBuilder = api.action("query")
508-
.param("generator", "categorymembers")
509-
.param("format", "xml")
510-
.param("gcmtype", "subcat")
511-
.param("gcmtitle", categoryName)
512-
.param("prop", "info")
513-
.param("gcmlimit", "500")
514-
.param("iiprop", "url|extmetadata");
515-
516-
apiResult = requestBuilder.get();
517-
} catch (IOException e) {
518-
Timber.e(e, "Failed to obtain searchCategories");
519-
}
520-
521-
if (apiResult == null) {
522-
return new ArrayList<>();
523-
}
524-
525-
CustomApiResult categoryImagesNode = apiResult.getNode("/api/query/pages");
526-
if (categoryImagesNode == null
527-
|| categoryImagesNode.getDocument() == null
528-
|| categoryImagesNode.getDocument().getChildNodes() == null
529-
|| categoryImagesNode.getDocument().getChildNodes().getLength() == 0) {
530-
return new ArrayList<>();
531-
}
532-
533-
NodeList childNodes = categoryImagesNode.getDocument().getChildNodes();
534-
return CategoryImageUtils.getSubCategoryList(childNodes);
535-
}
536-
537-
/**
538-
* The method takes categoryName as input and returns a List of parent categories
539-
* It uses the generator query API to get the parent categories of a category, 500 at a time.
540-
*
541-
* @param categoryName Category name as defined on commons
542-
* @return
543-
*/
544-
@Override
545-
@NonNull
546-
public List<String> getParentCategoryList(String categoryName) {
547-
CustomApiResult apiResult = null;
548-
try {
549-
CustomMwApi.RequestBuilder requestBuilder = api.action("query")
550-
.param("generator", "categories")
551-
.param("format", "xml")
552-
.param("titles", categoryName)
553-
.param("prop", "info")
554-
.param("cllimit", "500")
555-
.param("iiprop", "url|extmetadata");
556-
557-
apiResult = requestBuilder.get();
558-
} catch (IOException e) {
559-
Timber.e(e, "Failed to obtain parent Categories");
560-
}
561-
562-
if (apiResult == null) {
563-
return new ArrayList<>();
564-
}
565-
566-
CustomApiResult categoryImagesNode = apiResult.getNode("/api/query/pages");
567-
if (categoryImagesNode == null
568-
|| categoryImagesNode.getDocument() == null
569-
|| categoryImagesNode.getDocument().getChildNodes() == null
570-
|| categoryImagesNode.getDocument().getChildNodes().getLength() == 0) {
571-
return new ArrayList<>();
572-
}
573-
574-
NodeList childNodes = categoryImagesNode.getDocument().getChildNodes();
575-
return CategoryImageUtils.getSubCategoryList(childNodes);
576-
}
577-
578-
579-
/**
580-
* For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages
581-
* https://www.mediawiki.org/wiki/API:Raw_query_continue
582-
* After fetching images a page of image for a particular category, shared defaultKvStore are updated with the latest QueryContinue Values
583-
*
584-
* @param keyword
585-
* @param queryContinue
586-
*/
587-
private void setQueryContinueValues(String keyword, QueryContinue queryContinue) {
588-
defaultKvStore.putString(keyword, gson.toJson(queryContinue));
589-
}
590-
591-
/**
592-
* Before making a paginated API call, this method is called to get the latest query continue values to be used
593-
*
594-
* @param keyword
595-
* @return
596-
*/
597-
@Nullable
598-
private QueryContinue getQueryContinueValues(String keyword) {
599-
String queryContinueString = defaultKvStore.getString(keyword, null);
600-
return gson.fromJson(queryContinueString, QueryContinue.class);
601-
}
602487

603488
@Override
604489
@NonNull

app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java

-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ public interface MediaWikiApi {
3030

3131
String getCentralAuthToken() throws IOException;
3232

33-
List<String> getSubCategoryList(String categoryName);
34-
35-
List<String> getParentCategoryList(String categoryName);
36-
3733
@NonNull
3834
Single<UploadStash> uploadFile(String filename, InputStream file,
3935
long dataLength, Uri fileUri, Uri contentProviderUri,

0 commit comments

Comments
 (0)