mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-29 22:03:55 +01:00
Use data client for peer review calls (#2937)
* Use data client for peer review calls * With java docs * Optimise API calls and fix tests
This commit is contained in:
parent
51e2febc53
commit
6f9d69e63c
7 changed files with 205 additions and 168 deletions
|
|
@ -12,13 +12,20 @@ import android.view.View;
|
|||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.viewpagerindicator.CirclePageIndicator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||
|
|
@ -29,8 +36,6 @@ import fr.free.nrw.commons.utils.ViewUtil;
|
|||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class ReviewActivity extends AuthenticatedActivity {
|
||||
|
||||
|
|
@ -139,7 +144,11 @@ public class ReviewActivity extends AuthenticatedActivity {
|
|||
compositeDisposable.add(reviewHelper.getRandomMedia()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateImage));
|
||||
.subscribe(media -> {
|
||||
if (media != null) {
|
||||
updateImage(media);
|
||||
}
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,63 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.dataclient.mwapi.RecentChange;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class ReviewHelper {
|
||||
private static final int MAX_RANDOM_TRIES = 5;
|
||||
|
||||
private static final String[] imageExtensions = new String[]{".jpg", ".jpeg", ".png"};
|
||||
|
||||
private final OkHttpJsonApiClient okHttpJsonApiClient;
|
||||
private final MediaWikiApi mediaWikiApi;
|
||||
private final ReviewInterface reviewInterface;
|
||||
|
||||
@Inject
|
||||
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient, MediaWikiApi mediaWikiApi) {
|
||||
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient,
|
||||
MediaWikiApi mediaWikiApi,
|
||||
ReviewInterface reviewInterface) {
|
||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||
this.mediaWikiApi = mediaWikiApi;
|
||||
this.reviewInterface = reviewInterface;
|
||||
}
|
||||
|
||||
Single<Media> getRandomMedia() {
|
||||
return getRandomFileChange()
|
||||
.flatMap(fileName -> okHttpJsonApiClient.getMedia(fileName, false));
|
||||
/**
|
||||
* Fetches recent changes from MediaWiki API
|
||||
* Calls the API to get 10 changes in the last 1 hour
|
||||
* Earlier we were getting changes for the last 30 days but as the API returns just 10 results
|
||||
* its best to fetch for just last 1 hour.
|
||||
* @return
|
||||
*/
|
||||
private Observable<RecentChange> getRecentChanges() {
|
||||
final int RANDOM_SECONDS = 60 * 60;
|
||||
Random r = new Random();
|
||||
Date now = new Date();
|
||||
Date startDate = new Date(now.getTime() - r.nextInt(RANDOM_SECONDS) * 1000L);
|
||||
|
||||
String rcStart = DateUtil.iso8601DateFormat(startDate);
|
||||
return reviewInterface.getRecentChanges(rcStart)
|
||||
.map(mwQueryResponse -> mwQueryResponse.query().getRecentChanges())
|
||||
.map(recentChanges -> {
|
||||
//Collections.shuffle(recentChanges);
|
||||
return recentChanges;
|
||||
})
|
||||
.flatMapIterable(changes -> changes)
|
||||
.filter(recentChange -> isChangeReviewable(recentChange));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,57 +67,62 @@ public class ReviewHelper {
|
|||
* - Checks if the file is nominated for deletion
|
||||
* - Retries upto 5 times for getting a file which is not nominated for deletion
|
||||
*
|
||||
* @return Random file change
|
||||
*/
|
||||
public Single<Media> getRandomMedia() {
|
||||
return getRecentChanges()
|
||||
.flatMapSingle(change -> getRandomMediaFromRecentChange(change))
|
||||
.onExceptionResumeNext(Observable.just(new Media("")))
|
||||
.filter(media -> !StringUtils.isBlank(media.getFilename()))
|
||||
.firstOrError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proper Media object if the file is not already nominated for deletion
|
||||
* Else it returns an empty Media object
|
||||
* @param recentChange
|
||||
* @return
|
||||
*/
|
||||
private Single<String> getRandomFileChange() {
|
||||
return okHttpJsonApiClient.getRecentFileChanges()
|
||||
.map(this::findImageInRecentChanges)
|
||||
.flatMap(title -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + title)
|
||||
.map(pageExists -> new Pair<>(title, pageExists)))
|
||||
.map((Pair<String, Boolean> pair) -> {
|
||||
if (!pair.second) {
|
||||
return pair.first;
|
||||
private Single<Media> getRandomMediaFromRecentChange(RecentChange recentChange) {
|
||||
return Single.just(recentChange)
|
||||
.flatMap(change -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + change.getTitle()))
|
||||
.flatMap(isDeleted -> {
|
||||
if (isDeleted) {
|
||||
return Single.just(new Media(""));
|
||||
}
|
||||
throw new Exception("Already nominated for deletion");
|
||||
}).retry(MAX_RANDOM_TRIES);
|
||||
return okHttpJsonApiClient.getMedia(recentChange.getTitle(), false);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Single<MwQueryPage.Revision> getFirstRevisionOfFile(String fileName) {
|
||||
return okHttpJsonApiClient.getFirstRevisionOfFile(fileName);
|
||||
/**
|
||||
* Gets the first revision of the file from filename
|
||||
* @param filename
|
||||
* @return
|
||||
*/
|
||||
Observable<MwQueryPage.Revision> getFirstRevisionOfFile(String filename) {
|
||||
return reviewInterface.getFirstRevisionOfFile(filename)
|
||||
.map(response -> response.query().firstPage().revisions().get(0));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String findImageInRecentChanges(List<RecentChange> recentChanges) {
|
||||
String imageTitle;
|
||||
Random r = new Random();
|
||||
int count = recentChanges.size();
|
||||
// Build a range array
|
||||
int[] randomIndexes = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
randomIndexes[i] = i;
|
||||
/**
|
||||
* Checks if the change is reviewable or not.
|
||||
* - checks the type and revisionId of the change
|
||||
* - checks supported image extensions
|
||||
* @param recentChange
|
||||
* @return
|
||||
*/
|
||||
private boolean isChangeReviewable(RecentChange recentChange) {
|
||||
if (recentChange.getType().equals("log") && !(recentChange.getOldRevisionId() == 0)) {
|
||||
return false;
|
||||
}
|
||||
// Then shuffle it
|
||||
for (int i = 0; i < count; i++) {
|
||||
int swapIndex = r.nextInt(count);
|
||||
int temp = randomIndexes[i];
|
||||
randomIndexes[i] = randomIndexes[swapIndex];
|
||||
randomIndexes[swapIndex] = temp;
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
int randomIndex = randomIndexes[i];
|
||||
RecentChange recentChange = recentChanges.get(randomIndex);
|
||||
if (recentChange.getType().equals("log") && !(recentChange.getOldRevisionId() == 0)) {
|
||||
// For log entries, we only want ones where old_revid is zero, indicating a new file
|
||||
continue;
|
||||
}
|
||||
imageTitle = recentChange.getTitle();
|
||||
|
||||
for (String imageExtension : imageExtensions) {
|
||||
if (imageTitle.toLowerCase().endsWith(imageExtension)) {
|
||||
return imageTitle;
|
||||
}
|
||||
for (String extension : imageExtensions) {
|
||||
if (recentChange.getTitle().endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Interface class for peer review calls
|
||||
*/
|
||||
public interface ReviewInterface {
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2&list=recentchanges&rcprop=title|ids&rctype=new|log&rctoponly=1&rcnamespace=6")
|
||||
Observable<MwQueryResponse> getRecentChanges(@Query("rcstart") String rcStart);
|
||||
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2&prop=revisions&rvprop=timestamp|ids|user&rvdir=newer&rvlimit=1")
|
||||
Observable<MwQueryResponse> getFirstRevisionOfFile(@Query("titles") String titles);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue