From 00434cbbe289fd2a2c636f5f9cce5346d7df6f94 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Tue, 1 Mar 2022 05:11:31 +0530 Subject: [PATCH 01/10] Ask for category only if exists in Peer Review --- .../nrw/commons/review/ReviewActivity.java | 43 +++++++++++++++++-- .../free/nrw/commons/review/ReviewHelper.java | 5 +++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index 4839f69226..f6b8bb5d23 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -26,15 +26,25 @@ import com.viewpagerindicator.CirclePageIndicator; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.actions.PageEditClient; import fr.free.nrw.commons.delete.DeleteHelper; import fr.free.nrw.commons.media.MediaDetailFragment; import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.ViewUtil; +import io.reactivex.Single; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; +import java.util.ArrayList; import javax.inject.Inject; +import org.wikipedia.dataclient.Service; +import org.wikipedia.dataclient.ServiceFactory; +import org.wikipedia.dataclient.WikiSite; +import org.wikipedia.dataclient.mwapi.MwQueryPage.Category; +import org.wikipedia.dataclient.mwapi.MwQueryPage.CategoryInfo; +import org.wikipedia.json.GsonUtil; +import retrofit2.Retrofit; public class ReviewActivity extends BaseActivity { @@ -71,6 +81,8 @@ public class ReviewActivity extends BaseActivity { */ private ReviewImageFragment reviewImageFragment; + boolean categoryExists = false; + final String SAVED_MEDIA = "saved_media"; private Media media; @@ -153,15 +165,33 @@ public boolean onSupportNavigateUp() { @SuppressLint("CheckResult") public boolean runRandomizer() { + categoryExists = false; progressBar.setVisibility(View.VISIBLE); reviewPager.setCurrentItem(0); compositeDisposable.add(reviewHelper.getRandomMedia() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(media -> { - reviewImageFragment = getInstanceOfReviewImageFragment(); - reviewImageFragment.disableButtons(); - updateImage(media); + Service service = ServiceFactory.get(new WikiSite(Service.COMMONS_URL) + , Service.COMMONS_URL, Service.class); + + service.getCategories(media.getPageTitle().getDisplayText()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + for(Category category : response.query().firstPage().categories()) { + if(!category.hidden()) { + categoryExists = true; + break; + } + } + + reviewImageFragment = getInstanceOfReviewImageFragment(); + reviewImageFragment.disableButtons(); + updateImage(media); + }, error -> { + error.printStackTrace(); + }); })); return true; } @@ -195,8 +225,15 @@ private void updateImage(Media media) { public void swipeToNext() { int nextPos = reviewPager.getCurrentItem() + 1; + // If currently at category fragment, then check if category exists if (nextPos <= 3) { reviewPager.setCurrentItem(nextPos); + if (nextPos == 2) { + if (!categoryExists) { + swipeToNext(); + return; + } + } } else { runRandomizer(); } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java index 0402317929..22d1316422 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java @@ -122,4 +122,9 @@ private boolean isChangeReviewable(RecentChange recentChange) { return false; } + + private void getCategoriesInfoList() { + + } + } From bf37b84dd1ee681bfd5afba5003f830010909e22 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Tue, 1 Mar 2022 05:12:19 +0530 Subject: [PATCH 02/10] Minor Fixes --- .../nrw/commons/review/ReviewActivity.java | 22 +++++++++---------- .../free/nrw/commons/review/ReviewHelper.java | 5 ----- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index f6b8bb5d23..f585b311da 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -26,25 +26,19 @@ import com.viewpagerindicator.CirclePageIndicator; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.actions.PageEditClient; import fr.free.nrw.commons.delete.DeleteHelper; import fr.free.nrw.commons.media.MediaDetailFragment; import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.ViewUtil; -import io.reactivex.Single; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -import java.util.ArrayList; import javax.inject.Inject; import org.wikipedia.dataclient.Service; import org.wikipedia.dataclient.ServiceFactory; import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.mwapi.MwQueryPage.Category; -import org.wikipedia.dataclient.mwapi.MwQueryPage.CategoryInfo; -import org.wikipedia.json.GsonUtil; -import retrofit2.Retrofit; public class ReviewActivity extends BaseActivity { @@ -81,7 +75,10 @@ public class ReviewActivity extends BaseActivity { */ private ReviewImageFragment reviewImageFragment; - boolean categoryExists = false; + /** + * Flag to check whether there are any non-hidden categories in the File + */ + private boolean hasNonHiddenCategories = false; final String SAVED_MEDIA = "saved_media"; private Media media; @@ -165,13 +162,15 @@ public boolean onSupportNavigateUp() { @SuppressLint("CheckResult") public boolean runRandomizer() { - categoryExists = false; + hasNonHiddenCategories = false; progressBar.setVisibility(View.VISIBLE); reviewPager.setCurrentItem(0); compositeDisposable.add(reviewHelper.getRandomMedia() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(media -> { + // This REST API request will respond with + // category type (hidden/non-hidden) for each category in the file Service service = ServiceFactory.get(new WikiSite(Service.COMMONS_URL) , Service.COMMONS_URL, Service.class); @@ -181,7 +180,7 @@ public boolean runRandomizer() { .subscribe(response -> { for(Category category : response.query().firstPage().categories()) { if(!category.hidden()) { - categoryExists = true; + hasNonHiddenCategories = true; break; } } @@ -225,11 +224,12 @@ private void updateImage(Media media) { public void swipeToNext() { int nextPos = reviewPager.getCurrentItem() + 1; - // If currently at category fragment, then check if category exists + // If currently at category fragment, then check whether the media has any non-hidden category if (nextPos <= 3) { reviewPager.setCurrentItem(nextPos); if (nextPos == 2) { - if (!categoryExists) { + // The media has no non-hidden category. Such media are already flagged by server-side bots, so no need to review manually. + if (!hasNonHiddenCategories) { swipeToNext(); return; } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java index 22d1316422..0402317929 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java @@ -122,9 +122,4 @@ private boolean isChangeReviewable(RecentChange recentChange) { return false; } - - private void getCategoriesInfoList() { - - } - } From a3220940c2a16022a58124985d30ab75953034cc Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Thu, 3 Mar 2022 22:24:16 +0530 Subject: [PATCH 03/10] Fixed wrong categories issue --- .../main/java/fr/free/nrw/commons/Media.kt | 4 +- .../fr/free/nrw/commons/db/AppDatabase.kt | 2 +- .../commons/explore/media/MediaConverter.kt | 6 ++- .../fr/free/nrw/commons/media/MediaClient.kt | 1 + .../nrw/commons/media/MediaInterface.java | 6 ++- .../nrw/commons/review/ReviewActivity.java | 50 +++++++++++-------- .../commons/review/ReviewImageFragment.java | 15 +++++- 7 files changed, 58 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index 1a5531e852..23d8609a70 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -3,6 +3,7 @@ package fr.free.nrw.commons import android.os.Parcelable import fr.free.nrw.commons.location.LatLng import kotlinx.android.parcel.Parcelize +import org.wikipedia.dataclient.mwapi.MwQueryPage import org.wikipedia.page.PageTitle import java.util.* @@ -77,7 +78,8 @@ class Media constructor( var coordinates: LatLng? = null, var captions: Map = emptyMap(), var descriptions: Map = emptyMap(), - var depictionIds: List = emptyList() + var depictionIds: List = emptyList(), + var detailedCategories: Map = emptyMap() ) : Parcelable { constructor( diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt index 57a35ce708..b9a8795e0f 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt +++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt @@ -14,7 +14,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 10, exportSchema = false) +@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 11, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt index 2c140bc990..ad5268e19b 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt @@ -20,6 +20,9 @@ class MediaConverter @Inject constructor() { fun convert(page: MwQueryPage, entity: Entities.Entity, imageInfo: ImageInfo): Media { val metadata = imageInfo.metadata requireNotNull(metadata) { "No metadata" } + val myMap = mutableMapOf() + page.categories()?.forEach { myMap[it.title()] = ("" + it.hidden()) } + return Media( page.pageId().toString(), imageInfo.thumbUrl.takeIf { it.isNotBlank() } ?: imageInfo.originalUrl, @@ -35,7 +38,8 @@ class MediaConverter @Inject constructor() { metadata.latLng, entity.labels().mapValues { it.value.value() }, entity.descriptions().mapValues { it.value.value() }, - entity.depictionIds() + entity.depictionIds(), + myMap ) } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt index b4ab6ec01b..f2c85d27ec 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt @@ -186,6 +186,7 @@ class MediaClient @Inject constructor( .map { pages.zip(it.entities().values) .mapNotNull { (page, entity) -> + println(entity.labels().values.joinToString("|") + " " + entity.id()) page.imageInfo()?.let { mediaConverter.convert(page, entity, it) } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java index c354ba78bf..8a07688190 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java @@ -16,6 +16,10 @@ public interface MediaInterface { "&iiextmetadatafilter=DateTime|Categories|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal" + "|Artist|LicenseShortName|LicenseUrl"; + String MEDIA_PARAMS_CATEGORIES="&clprop=hidden&prop=categories|imageinfo&iiprop=url|extmetadata|user&&iiurlwidth=640" + + "&iiextmetadatafilter=DateTime|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal" + + "|Artist|LicenseShortName|LicenseUrl"; + /** * Checks if a page exists or not. * @@ -81,7 +85,7 @@ Single getMediaListForUser(@Query("gaiuser") String username, * @return */ @GET("w/api.php?action=query&format=json&formatversion=2" + - MEDIA_PARAMS) + MEDIA_PARAMS_CATEGORIES) Single getMedia(@Query("titles") String title); /** diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index f585b311da..965dc4ddaf 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -34,6 +34,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; +import java.util.Iterator; import javax.inject.Inject; import org.wikipedia.dataclient.Service; import org.wikipedia.dataclient.ServiceFactory; @@ -171,26 +172,35 @@ public boolean runRandomizer() { .subscribe(media -> { // This REST API request will respond with // category type (hidden/non-hidden) for each category in the file - Service service = ServiceFactory.get(new WikiSite(Service.COMMONS_URL) - , Service.COMMONS_URL, Service.class); - - service.getCategories(media.getPageTitle().getDisplayText()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> { - for(Category category : response.query().firstPage().categories()) { - if(!category.hidden()) { - hasNonHiddenCategories = true; - break; - } - } - - reviewImageFragment = getInstanceOfReviewImageFragment(); - reviewImageFragment.disableButtons(); - updateImage(media); - }, error -> { - error.printStackTrace(); - }); + for(String key : media.getDetailedCategories().keySet()) { + String value = media.getDetailedCategories().get(key); + if(value.equals("false")) { + hasNonHiddenCategories = true; + break; + } + } + reviewImageFragment = getInstanceOfReviewImageFragment(); + reviewImageFragment.disableButtons(); + updateImage(media); + +// Service service = ServiceFactory.get(new WikiSite(Service.COMMONS_URL) +// , Service.COMMONS_URL, Service.class); +// +// service.getCategories(media.getPageTitle().getDisplayText()) +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(response -> { +// for(Category category : response.query().firstPage().categories()) { +// if(!category.hidden()) { +// hasNonHiddenCategories = true; +// break; +// } +// } +// +// +// }, error -> { +// error.printStackTrace(); +// }); })); return true; } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java index b913c1aa9a..44439750b1 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java @@ -19,6 +19,8 @@ import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import java.util.ArrayList; +import java.util.List; public class ReviewImageFragment extends CommonsDaggerSupportFragment { @@ -52,8 +54,17 @@ public void update(int position) { private String updateCategoriesQuestion() { Media media = getReviewActivity().getMedia(); - if (media != null && media.getCategories() != null && isAdded()) { - String catString = TextUtils.join(", ", media.getCategories()); + if (media != null && media.getDetailedCategories() != null && isAdded()) { + List categories = new ArrayList<>(); + for(String key : media.getDetailedCategories().keySet()) { + String value = String.valueOf(key); + int index = key.indexOf("Category:"); + if(index == 0) { + value = key.substring(9); + } + categories.add(value); + } + String catString = TextUtils.join(", ", categories); if (catString != null && !catString.equals("") && textViewQuestionContext != null) { catString = "" + catString + ""; String stringToConvertHtml = String.format(getResources().getString(R.string.review_category_explanation), catString); From 781e99030f69e70aa58c4a00420628d9124b3c11 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Thu, 3 Mar 2022 22:39:00 +0530 Subject: [PATCH 04/10] Added comments --- .../main/java/fr/free/nrw/commons/Media.kt | 5 +++- .../fr/free/nrw/commons/db/AppDatabase.kt | 2 +- .../commons/explore/media/MediaConverter.kt | 1 + .../fr/free/nrw/commons/media/MediaClient.kt | 1 - .../nrw/commons/media/MediaInterface.java | 8 ++++--- .../nrw/commons/review/ReviewActivity.java | 24 +++---------------- .../commons/review/ReviewImageFragment.java | 3 +++ 7 files changed, 17 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index 23d8609a70..9d617ca169 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -79,7 +79,10 @@ class Media constructor( var captions: Map = emptyMap(), var descriptions: Map = emptyMap(), var depictionIds: List = emptyList(), - var detailedCategories: Map = emptyMap() + /** + * Stores the mapping of category title to hidden attribute + */ + var detailedCategories: Map = emptyMap() // This field was added to find non-hidden categories ) : Parcelable { constructor( diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt index b9a8795e0f..57a35ce708 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt +++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt @@ -14,7 +14,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 11, exportSchema = false) +@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 10, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt index ad5268e19b..8d1cc5f2f4 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt @@ -20,6 +20,7 @@ class MediaConverter @Inject constructor() { fun convert(page: MwQueryPage, entity: Entities.Entity, imageInfo: ImageInfo): Media { val metadata = imageInfo.metadata requireNotNull(metadata) { "No metadata" } + // Stores mapping of title attribute to hidden attribute of each category val myMap = mutableMapOf() page.categories()?.forEach { myMap[it.title()] = ("" + it.hidden()) } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt index f2c85d27ec..b4ab6ec01b 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt @@ -186,7 +186,6 @@ class MediaClient @Inject constructor( .map { pages.zip(it.entities().values) .mapNotNull { (page, entity) -> - println(entity.labels().values.joinToString("|") + " " + entity.id()) page.imageInfo()?.let { mediaConverter.convert(page, entity, it) } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java index 8a07688190..e56c3733aa 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.java @@ -3,7 +3,6 @@ import io.reactivex.Single; import java.util.Map; import org.wikipedia.dataclient.mwapi.MwQueryResponse; -import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; import retrofit2.http.QueryMap; @@ -16,7 +15,10 @@ public interface MediaInterface { "&iiextmetadatafilter=DateTime|Categories|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal" + "|Artist|LicenseShortName|LicenseUrl"; - String MEDIA_PARAMS_CATEGORIES="&clprop=hidden&prop=categories|imageinfo&iiprop=url|extmetadata|user&&iiurlwidth=640" + + /** + * fetches category detail(title, hidden) for each category along with File information + */ + String MEDIA_PARAMS_WITH_CATEGORY_DETAILS ="&clprop=hidden&prop=categories|imageinfo&iiprop=url|extmetadata|user&&iiurlwidth=640" + "&iiextmetadatafilter=DateTime|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal" + "|Artist|LicenseShortName|LicenseUrl"; @@ -85,7 +87,7 @@ Single getMediaListForUser(@Query("gaiuser") String username, * @return */ @GET("w/api.php?action=query&format=json&formatversion=2" + - MEDIA_PARAMS_CATEGORIES) + MEDIA_PARAMS_WITH_CATEGORY_DETAILS) Single getMedia(@Query("titles") String title); /** diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index 965dc4ddaf..9f7e810798 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -170,10 +170,11 @@ public boolean runRandomizer() { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(media -> { - // This REST API request will respond with - // category type (hidden/non-hidden) for each category in the file + // Finds non-hidden categories from Media instance for(String key : media.getDetailedCategories().keySet()) { String value = media.getDetailedCategories().get(key); + // If non-hidden category is found then set hasNonHiddenCategories to true + // so that category review cannot be skipped if(value.equals("false")) { hasNonHiddenCategories = true; break; @@ -182,25 +183,6 @@ public boolean runRandomizer() { reviewImageFragment = getInstanceOfReviewImageFragment(); reviewImageFragment.disableButtons(); updateImage(media); - -// Service service = ServiceFactory.get(new WikiSite(Service.COMMONS_URL) -// , Service.COMMONS_URL, Service.class); -// -// service.getCategories(media.getPageTitle().getDisplayText()) -// .subscribeOn(Schedulers.io()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribe(response -> { -// for(Category category : response.query().firstPage().categories()) { -// if(!category.hidden()) { -// hasNonHiddenCategories = true; -// break; -// } -// } -// -// -// }, error -> { -// error.printStackTrace(); -// }); })); return true; } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java index 44439750b1..912e9ac7b0 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java @@ -55,9 +55,12 @@ public void update(int position) { private String updateCategoriesQuestion() { Media media = getReviewActivity().getMedia(); if (media != null && media.getDetailedCategories() != null && isAdded()) { + // Filter category name attribute from all categories List categories = new ArrayList<>(); for(String key : media.getDetailedCategories().keySet()) { String value = String.valueOf(key); + // Each category returned has a format like "Category:" + // so remove the prefix "Category:" int index = key.indexOf("Category:"); if(index == 0) { value = key.substring(9); From f882a0f16cf016eee755105583b83b7565376cc5 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Fri, 4 Mar 2022 03:36:16 +0530 Subject: [PATCH 05/10] Added comments --- app/src/main/java/fr/free/nrw/commons/Media.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index 9d617ca169..fbe75565f4 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -80,9 +80,11 @@ class Media constructor( var descriptions: Map = emptyMap(), var depictionIds: List = emptyList(), /** + * This field was added to find non-hidden categories * Stores the mapping of category title to hidden attribute + * Example: Mountains => false, CC-BY-SA-2.0 => true */ - var detailedCategories: Map = emptyMap() // This field was added to find non-hidden categories + var detailedCategories: Map = emptyMap() ) : Parcelable { constructor( From 0f3eed061b07077530f56f5defb6058efddc26d2 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Fri, 4 Mar 2022 19:05:45 +0530 Subject: [PATCH 06/10] Minor Changes --- app/src/main/java/fr/free/nrw/commons/Media.kt | 4 ++-- .../main/java/fr/free/nrw/commons/db/AppDatabase.kt | 2 +- .../main/java/fr/free/nrw/commons/db/Converters.java | 10 ++++++++++ .../free/nrw/commons/explore/media/MediaConverter.kt | 4 ++-- .../fr/free/nrw/commons/review/ReviewActivity.java | 11 +++-------- .../free/nrw/commons/review/ReviewImageFragment.java | 4 ++-- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index fbe75565f4..af78ae6dd8 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -82,9 +82,9 @@ class Media constructor( /** * This field was added to find non-hidden categories * Stores the mapping of category title to hidden attribute - * Example: Mountains => false, CC-BY-SA-2.0 => true + * Example: "Mountains" => false, "CC-BY-SA-2.0" => true */ - var detailedCategories: Map = emptyMap() + var categoriesHiddenStatus: Map = emptyMap() ) : Parcelable { constructor( diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt index 57a35ce708..b9a8795e0f 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt +++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt @@ -14,7 +14,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 10, exportSchema = false) +@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 11, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao diff --git a/app/src/main/java/fr/free/nrw/commons/db/Converters.java b/app/src/main/java/fr/free/nrw/commons/db/Converters.java index 59b06a6751..6f0c8c1fc0 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/Converters.java +++ b/app/src/main/java/fr/free/nrw/commons/db/Converters.java @@ -79,11 +79,21 @@ public static String mapObjectToString(Map objectList) { return writeObjectToString(objectList); } + @TypeConverter + public static String mapObjectToString2(Map objectList) { + return writeObjectToString(objectList); + } + @TypeConverter public static Map stringToMap(String objectList) { return readObjectWithTypeToken(objectList, new TypeToken>(){}); } + @TypeConverter + public static Map stringToMap2(String objectList) { + return readObjectWithTypeToken(objectList, new TypeToken>(){}); + } + @TypeConverter public static String latlngObjectToString(LatLng latlng) { return writeObjectToString(latlng); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt index 8d1cc5f2f4..11101db234 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt @@ -21,8 +21,8 @@ class MediaConverter @Inject constructor() { val metadata = imageInfo.metadata requireNotNull(metadata) { "No metadata" } // Stores mapping of title attribute to hidden attribute of each category - val myMap = mutableMapOf() - page.categories()?.forEach { myMap[it.title()] = ("" + it.hidden()) } + val myMap = mutableMapOf() + page.categories()?.forEach { myMap[it.title()] = (it.hidden()) } return Media( page.pageId().toString(), diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index 9f7e810798..9dae970454 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -34,12 +34,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -import java.util.Iterator; import javax.inject.Inject; -import org.wikipedia.dataclient.Service; -import org.wikipedia.dataclient.ServiceFactory; -import org.wikipedia.dataclient.WikiSite; -import org.wikipedia.dataclient.mwapi.MwQueryPage.Category; public class ReviewActivity extends BaseActivity { @@ -171,11 +166,11 @@ public boolean runRandomizer() { .observeOn(AndroidSchedulers.mainThread()) .subscribe(media -> { // Finds non-hidden categories from Media instance - for(String key : media.getDetailedCategories().keySet()) { - String value = media.getDetailedCategories().get(key); + for(String key : media.getCategoriesHiddenStatus().keySet()) { + Boolean value = media.getCategoriesHiddenStatus().get(key); // If non-hidden category is found then set hasNonHiddenCategories to true // so that category review cannot be skipped - if(value.equals("false")) { + if(!value) { hasNonHiddenCategories = true; break; } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java index 912e9ac7b0..7391d6b2da 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewImageFragment.java @@ -54,10 +54,10 @@ public void update(int position) { private String updateCategoriesQuestion() { Media media = getReviewActivity().getMedia(); - if (media != null && media.getDetailedCategories() != null && isAdded()) { + if (media != null && media.getCategoriesHiddenStatus() != null && isAdded()) { // Filter category name attribute from all categories List categories = new ArrayList<>(); - for(String key : media.getDetailedCategories().keySet()) { + for(String key : media.getCategoriesHiddenStatus().keySet()) { String value = String.valueOf(key); // Each category returned has a format like "Category:" // so remove the prefix "Category:" From e723b265fac268ee2659e1b81b0704c86793996a Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Fri, 4 Mar 2022 18:42:01 +0530 Subject: [PATCH 07/10] Added test --- .../nrw/commons/review/ReviewActivityTest.kt | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt index 7e0a3b7f90..ffededc345 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt @@ -1,21 +1,30 @@ package fr.free.nrw.commons.review import android.content.Context +import android.os.Looper.getMainLooper import android.view.Menu import android.view.MenuItem +import butterknife.BindView import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.soloader.SoLoader +import com.nhaarman.mockitokotlin2.doNothing import fr.free.nrw.commons.TestAppAdapter import fr.free.nrw.commons.TestCommonsApplication import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.doReturn import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode import org.robolectric.fakes.RoboMenu import org.robolectric.fakes.RoboMenuItem import org.wikipedia.AppAdapter @@ -23,6 +32,7 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) +@LooperMode(LooperMode.Mode.PAUSED) class ReviewActivityTest { private lateinit var activity: ReviewActivity @@ -33,6 +43,11 @@ class ReviewActivityTest { private lateinit var context: Context + @Mock + var reviewPager: ReviewViewPager? = null + + var hasNonHiddenCategories: Boolean = false + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -50,7 +65,8 @@ class ReviewActivityTest { menuItem = RoboMenuItem(null) menu = RoboMenu(context) - + Whitebox.setInternalState(activity, "reviewPager", reviewPager); + Whitebox.setInternalState(activity, "hasNonHiddenCategories", hasNonHiddenCategories); } @@ -69,6 +85,8 @@ class ReviewActivityTest { @Test @Throws(Exception::class) fun testSwipeToNext() { + shadowOf(getMainLooper()).idle() + doReturn(1,2).`when`(reviewPager)?.currentItem activity.swipeToNext() } From 15fe99c3270462d295f91df62dde9f9357d86de0 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Sat, 12 Mar 2022 05:46:37 +0530 Subject: [PATCH 08/10] Tests --- .../nrw/commons/review/ReviewActivityTest.kt | 30 +++++++++++++++++-- .../commons/review/ReviewImageFragmentTest.kt | 17 +++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt index ffededc345..bd5fcc0728 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt @@ -8,15 +8,19 @@ import butterknife.BindView import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.soloader.SoLoader import com.nhaarman.mockitokotlin2.doNothing +import fr.free.nrw.commons.Media import fr.free.nrw.commons.TestAppAdapter import fr.free.nrw.commons.TestCommonsApplication +import io.reactivex.Scheduler +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.Mockito.doReturn +import org.mockito.Mockito.* import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox import org.robolectric.Robolectric @@ -48,6 +52,9 @@ class ReviewActivityTest { var hasNonHiddenCategories: Boolean = false + @Mock + var reviewHelper: ReviewHelper? = null + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -67,7 +74,7 @@ class ReviewActivityTest { menu = RoboMenu(context) Whitebox.setInternalState(activity, "reviewPager", reviewPager); Whitebox.setInternalState(activity, "hasNonHiddenCategories", hasNonHiddenCategories); - + Whitebox.setInternalState(activity, "reviewHelper", reviewHelper); } @Test @@ -90,6 +97,23 @@ class ReviewActivityTest { activity.swipeToNext() } + @Test + @Throws(Exception::class) + fun testSwipeToLastFragment() { + shadowOf(getMainLooper()).idle() + doReturn(3).`when`(reviewPager)?.currentItem + val media = mock(Media::class.java) + + doReturn(mapOf("test" to false)).`when`(media).categoriesHiddenStatus + doReturn(Single.just(media)).`when`(reviewHelper)?.randomMedia + Assert.assertNotNull(reviewHelper?.randomMedia) + reviewHelper + ?.randomMedia + ?.test() + ?.assertValue(media); + activity.swipeToNext() + } + @Test @Throws(Exception::class) fun testOnDestroy() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt index 904dbca37f..a6b021829d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt @@ -11,6 +11,8 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.soloader.SoLoader +import com.nhaarman.mockitokotlin2.doReturn +import fr.free.nrw.commons.Media import fr.free.nrw.commons.R import fr.free.nrw.commons.TestAppAdapter import fr.free.nrw.commons.TestCommonsApplication @@ -20,7 +22,10 @@ import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment @@ -51,6 +56,7 @@ class ReviewImageFragmentTest { @Mock private lateinit var savedInstanceState: Bundle + private lateinit var activity: ReviewActivity @Before fun setUp() { @@ -61,7 +67,7 @@ class ReviewImageFragmentTest { SoLoader.setInTestMode() Fresco.initialize(context) - val activity = Robolectric.buildActivity(ReviewActivity::class.java).create().get() + activity = Robolectric.buildActivity(ReviewActivity::class.java).create().get() fragment = ReviewImageFragment() val bundle = Bundle() bundle.putInt("position", 1) @@ -110,10 +116,17 @@ class ReviewImageFragmentTest { @Test @Throws(Exception::class) fun testOnUpdateCategoriesQuestion() { + shadowOf(Looper.getMainLooper()).idle() + val media = mock(Media::class.java) + Whitebox.setInternalState(activity, "media", media) + Assert.assertNotNull(media) + val categories = mapOf("Category:" to false) + doReturn(categories).`when`(media).categoriesHiddenStatus + Assert.assertNotNull(media.categoriesHiddenStatus) + Assert.assertNotNull(fragment.isAdded) val method: Method = ReviewImageFragment::class.java.getDeclaredMethod("updateCategoriesQuestion") method.isAccessible = true - shadowOf(Looper.getMainLooper()).idle() method.invoke(fragment) } From fbbbe495ef6b69785d5b10ab07ce781eb04167eb Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Sat, 12 Mar 2022 05:46:47 +0530 Subject: [PATCH 09/10] Tests From 79d6831a290129c63838acba6ddf6192a134b842 Mon Sep 17 00:00:00 2001 From: devarsh-mavani-19 Date: Wed, 16 Mar 2022 21:06:25 +0530 Subject: [PATCH 10/10] Tests --- .../nrw/commons/review/ReviewActivity.java | 31 ++++++++++++------- .../nrw/commons/review/ReviewActivityTest.kt | 30 ++++++++++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index 9dae970454..d291815eab 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -166,22 +166,29 @@ public boolean runRandomizer() { .observeOn(AndroidSchedulers.mainThread()) .subscribe(media -> { // Finds non-hidden categories from Media instance - for(String key : media.getCategoriesHiddenStatus().keySet()) { - Boolean value = media.getCategoriesHiddenStatus().get(key); - // If non-hidden category is found then set hasNonHiddenCategories to true - // so that category review cannot be skipped - if(!value) { - hasNonHiddenCategories = true; - break; - } - } - reviewImageFragment = getInstanceOfReviewImageFragment(); - reviewImageFragment.disableButtons(); - updateImage(media); + findNonHiddenCategories(media); })); return true; } + /** + * Finds non-hidden categories and updates current image + */ + private void findNonHiddenCategories(Media media) { + for(String key : media.getCategoriesHiddenStatus().keySet()) { + Boolean value = media.getCategoriesHiddenStatus().get(key); + // If non-hidden category is found then set hasNonHiddenCategories to true + // so that category review cannot be skipped + if(!value) { + hasNonHiddenCategories = true; + break; + } + } + reviewImageFragment = getInstanceOfReviewImageFragment(); + reviewImageFragment.disableButtons(); + updateImage(media); + } + @SuppressLint("CheckResult") private void updateImage(Media media) { this.media = media; diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt index bd5fcc0728..a7a368152b 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt @@ -4,6 +4,7 @@ import android.content.Context import android.os.Looper.getMainLooper import android.view.Menu import android.view.MenuItem +import android.widget.Button import butterknife.BindView import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.soloader.SoLoader @@ -19,9 +20,11 @@ import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.MockitoAnnotations +import org.mockito.Spy import org.powermock.reflect.Whitebox import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner @@ -47,6 +50,9 @@ class ReviewActivityTest { private lateinit var context: Context + @Mock + private lateinit var reviewPagerAdapter: ReviewPagerAdapter + @Mock var reviewPager: ReviewViewPager? = null @@ -55,6 +61,9 @@ class ReviewActivityTest { @Mock var reviewHelper: ReviewHelper? = null + @Mock + private lateinit var reviewImageFragment: ReviewImageFragment + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -75,6 +84,9 @@ class ReviewActivityTest { Whitebox.setInternalState(activity, "reviewPager", reviewPager); Whitebox.setInternalState(activity, "hasNonHiddenCategories", hasNonHiddenCategories); Whitebox.setInternalState(activity, "reviewHelper", reviewHelper); + Whitebox.setInternalState(activity, "reviewImageFragment", reviewImageFragment); + Whitebox.setInternalState(activity, "reviewPagerAdapter", reviewPagerAdapter); + } @Test @@ -114,6 +126,24 @@ class ReviewActivityTest { activity.swipeToNext() } + @Test + @Throws(Exception::class) + fun testFindNonHiddenCategories() { + shadowOf(getMainLooper()).idle() + val media = mock(Media::class.java) + doReturn(mapOf("test" to false)).`when`(media).categoriesHiddenStatus + doReturn(mock(ReviewImageFragment::class.java)).`when`(reviewPagerAdapter).instantiateItem(ArgumentMatchers.any(), anyInt()) + doReturn("").`when`(media).filename + doNothing().`when`(reviewImageFragment).disableButtons() + + var findNonHiddenCategory: Method = + ReviewActivity::class.java.getDeclaredMethod("findNonHiddenCategories" + , Media::class.java) + findNonHiddenCategory.isAccessible = true + findNonHiddenCategory.invoke(activity, media) + + } + @Test @Throws(Exception::class) fun testOnDestroy() {