Review category issues (#4897)

* Ask for category only if exists in Peer Review

* Minor Fixes

* Fixed wrong categories issue

* Added comments

* Added comments

* Minor Changes

* Added test

* Tests

* Tests

* Tests
This commit is contained in:
Devarsh Mavani 2022-03-17 15:42:30 +05:30 committed by GitHub
parent 7bc78f67ff
commit 103e2d546e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 171 additions and 13 deletions

View file

@ -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,13 @@ class Media constructor(
var coordinates: LatLng? = null,
var captions: Map<String, String> = emptyMap(),
var descriptions: Map<String, String> = emptyMap(),
var depictionIds: List<String> = emptyList()
var depictionIds: List<String> = 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 categoriesHiddenStatus: Map<String, Boolean> = emptyMap()
) : Parcelable {
constructor(

View file

@ -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

View file

@ -79,11 +79,21 @@ public class Converters {
return writeObjectToString(objectList);
}
@TypeConverter
public static String mapObjectToString2(Map<String,Boolean> objectList) {
return writeObjectToString(objectList);
}
@TypeConverter
public static Map<String,String> stringToMap(String objectList) {
return readObjectWithTypeToken(objectList, new TypeToken<Map<String,String>>(){});
}
@TypeConverter
public static Map<String,Boolean> stringToMap2(String objectList) {
return readObjectWithTypeToken(objectList, new TypeToken<Map<String,Boolean>>(){});
}
@TypeConverter
public static String latlngObjectToString(LatLng latlng) {
return writeObjectToString(latlng);

View file

@ -20,6 +20,10 @@ 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<String, Boolean>()
page.categories()?.forEach { myMap[it.title()] = (it.hidden()) }
return Media(
page.pageId().toString(),
imageInfo.thumbUrl.takeIf { it.isNotBlank() } ?: imageInfo.originalUrl,
@ -35,7 +39,8 @@ class MediaConverter @Inject constructor() {
metadata.latLng,
entity.labels().mapValues { it.value.value() },
entity.descriptions().mapValues { it.value.value() },
entity.depictionIds()
entity.depictionIds(),
myMap
)
}

View file

@ -3,7 +3,6 @@ package fr.free.nrw.commons.media;
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,6 +15,13 @@ public interface MediaInterface {
"&iiextmetadatafilter=DateTime|Categories|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal" +
"|Artist|LicenseShortName|LicenseUrl";
/**
* 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";
/**
* Checks if a page exists or not.
*
@ -81,7 +87,7 @@ public interface MediaInterface {
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2" +
MEDIA_PARAMS)
MEDIA_PARAMS_WITH_CATEGORY_DETAILS)
Single<MwQueryResponse> getMedia(@Query("titles") String title);
/**

View file

@ -71,6 +71,11 @@ public class ReviewActivity extends BaseActivity {
*/
private ReviewImageFragment reviewImageFragment;
/**
* 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;
@ -153,19 +158,37 @@ public class ReviewActivity extends BaseActivity {
@SuppressLint("CheckResult")
public boolean runRandomizer() {
hasNonHiddenCategories = 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);
// Finds non-hidden categories from Media instance
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;
@ -195,8 +218,16 @@ public class ReviewActivity extends BaseActivity {
public void swipeToNext() {
int nextPos = reviewPager.getCurrentItem() + 1;
// If currently at category fragment, then check whether the media has any non-hidden category
if (nextPos <= 3) {
reviewPager.setCurrentItem(nextPos);
if (nextPos == 2) {
// 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;
}
}
} else {
runRandomizer();
}

View file

@ -19,6 +19,8 @@ import butterknife.OnClick;
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,20 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment {
private String updateCategoriesQuestion() {
Media media = getReviewActivity().getMedia();
if (media != null && media.getCategories() != null && isAdded()) {
String catString = TextUtils.join(", ", media.getCategories());
if (media != null && media.getCategoriesHiddenStatus() != null && isAdded()) {
// Filter category name attribute from all categories
List<String> categories = new ArrayList<>();
for(String key : media.getCategoriesHiddenStatus().keySet()) {
String value = String.valueOf(key);
// Each category returned has a format like "Category:<some-category-name>"
// so remove the prefix "Category:"
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 = "<b>" + catString + "</b>";
String stringToConvertHtml = String.format(getResources().getString(R.string.review_category_explanation), catString);