Set Media legend for wikidata entity (#3838)

* Set media legends and P18

* Minor

* Make media legends work

* Add test cases

* Use statement partial

* With minor refactoring

* Fix build
This commit is contained in:
Vivek Maskara 2020-06-30 05:11:27 -07:00 committed by GitHub
parent 7caf73fb4b
commit f26784e9c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 195 additions and 199 deletions

View file

@ -76,7 +76,7 @@ dependencies {
testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5" testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
// Unit testing // Unit testing
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.13'
testImplementation 'org.robolectric:robolectric:4.3' testImplementation 'org.robolectric:robolectric:4.3'
testImplementation 'androidx.test:core:1.2.0' testImplementation 'androidx.test:core:1.2.0'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1' testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'

View file

@ -31,6 +31,7 @@ import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.media.MediaClient; import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.wikidata.WikidataEditService;
import java.util.Locale; import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -41,7 +42,8 @@ import org.wikipedia.dataclient.WikiSite;
*/ */
public class ContributionsListFragment extends CommonsDaggerSupportFragment implements public class ContributionsListFragment extends CommonsDaggerSupportFragment implements
ContributionsListContract.View, ContributionsListAdapter.Callback, WikipediaInstructionsDialogFragment.Callback { ContributionsListContract.View, ContributionsListAdapter.Callback,
WikipediaInstructionsDialogFragment.Callback {
private static final String RV_STATE = "rv_scroll_state"; private static final String RV_STATE = "rv_scroll_state";
@ -275,7 +277,6 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
} }
public Media getMediaAtPosition(final int i) { public Media getMediaAtPosition(final int i) {
return adapter.getContributionForPosition(i); return adapter.getContributionForPosition(i);
} }
@ -291,13 +292,14 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
*/ */
@Override @Override
public void onConfirmClicked(@Nullable Contribution contribution, boolean copyWikicode) { public void onConfirmClicked(@Nullable Contribution contribution, boolean copyWikicode) {
if(copyWikicode) { if (copyWikicode) {
String wikicode = contribution.getWikiCode(); String wikicode = contribution.getWikiCode();
Utils.copy("wikicode", wikicode, getContext()); Utils.copy("wikicode", wikicode, getContext());
} }
final String url = languageWikipediaSite.mobileUrl() + "/wiki/" + contribution.getWikidataPlace() final String url =
.getWikipediaPageTitle(); languageWikipediaSite.mobileUrl() + "/wiki/" + contribution.getWikidataPlace()
.getWikipediaPageTitle();
Utils.handleWebUrl(getContext(), Uri.parse(url)); Utils.handleWebUrl(getContext(), Uri.parse(url));
} }

View file

@ -21,24 +21,16 @@ import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsApplicationModule; import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.di.CommonsDaggerService; import fr.free.nrw.commons.di.CommonsDaggerService;
import fr.free.nrw.commons.media.MediaClient; import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.wikidata.WikidataEditService; import fr.free.nrw.commons.wikidata.WikidataEditService;
import io.reactivex.Completable;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Scheduler; import io.reactivex.Scheduler;
import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.processors.PublishProcessor; import io.reactivex.processors.PublishProcessor;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
@ -313,7 +305,7 @@ public class UploadService extends CommonsDaggerService {
.add(wikidataEditService.addDepictionsAndCaptions(uploadResult, contribution)); .add(wikidataEditService.addDepictionsAndCaptions(uploadResult, contribution));
WikidataPlace wikidataPlace = contribution.getWikidataPlace(); WikidataPlace wikidataPlace = contribution.getWikidataPlace();
if (wikidataPlace != null && wikidataPlace.getImageValue() == null) { if (wikidataPlace != null && wikidataPlace.getImageValue() == null) {
wikidataEditService.createImageClaim(wikidataPlace, uploadResult); wikidataEditService.createClaim(wikidataPlace, uploadResult.getFilename(), contribution.getCaptions());
} }
saveCompletedContribution(contribution, uploadResult); saveCompletedContribution(contribution, uploadResult);
} }

View file

@ -1,6 +1,6 @@
package fr.free.nrw.commons.wikidata; package fr.free.nrw.commons.wikidata;
import fr.free.nrw.commons.upload.WikidataItem; import com.google.gson.Gson;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.inject.Inject; import javax.inject.Inject;
@ -9,64 +9,38 @@ import javax.inject.Singleton;
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse; import fr.free.nrw.commons.wikidata.model.AddEditTagResponse;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.ObservableSource; import io.reactivex.ObservableSource;
import okhttp3.MediaType; import org.wikipedia.wikidata.Statement_partial;
import okhttp3.RequestBody;
@Singleton @Singleton
public class WikidataClient { public class WikidataClient {
private final WikidataInterface wikidataInterface; private final WikidataInterface wikidataInterface;
private final Gson gson;
@Inject @Inject
public WikidataClient(WikidataInterface wikidataInterface) { public WikidataClient(WikidataInterface wikidataInterface, final Gson gson) {
this.wikidataInterface = wikidataInterface; this.wikidataInterface = wikidataInterface;
} this.gson = gson;
}
/** /**
* Create wikidata claim to add P18 value * Create wikidata claim to add P18 value
* @param entity wikidata entity ID *
* @param value value of the P18 edit * @return revisionID of the edit
* @return revisionID of the edit */
*/ Observable<Long> setClaim(Statement_partial claim, String tags) {
Observable<Long> createImageClaim(WikidataItem entity, String value) { return getCsrfToken()
return getCsrfToken() .flatMap(csrfToken -> wikidataInterface.postSetClaim(gson.toJson(claim), tags, csrfToken))
.flatMap(csrfToken -> wikidataInterface.postCreateClaim( .map(mwPostResponse -> mwPostResponse.getPageinfo().getLastrevid());
toRequestBody(entity.getId()), }
toRequestBody("value"),
toRequestBody(WikidataProperties.IMAGE.getPropertyName()),
toRequestBody(value),
toRequestBody("en"),
toRequestBody(csrfToken)))
.map(mwPostResponse -> mwPostResponse.getPageinfo().getLastrevid());
}
/** /**
* Converts string value to RequestBody for multipart request * Get csrf token for wikidata edit
*/ */
private RequestBody toRequestBody(String value) { @NotNull
return RequestBody.create(MediaType.parse("text/plain"), value); private Observable<String> getCsrfToken() {
} return wikidataInterface.getCsrfToken()
.map(mwQueryResponse -> mwQueryResponse.query().csrfToken());
/** }
* Get csrf token for wikidata edit
*/
@NotNull
private Observable<String> getCsrfToken() {
return wikidataInterface.getCsrfToken().map(mwQueryResponse -> mwQueryResponse.query().csrfToken());
}
/**
* Add edit tag for a given revision ID. The app currently uses this to tag P18 edits
* @param revisionId revision ID of the page edited
* @param tag to be added
* @param reason to be mentioned
*/
ObservableSource<AddEditTagResponse> addEditTag(Long revisionId, String tag, String reason) {
return getCsrfToken()
.flatMap(csrfToken -> wikidataInterface.addEditTag(String.valueOf(revisionId),
tag,
reason,
csrfToken));
}
} }

View file

@ -20,49 +20,58 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.wikipedia.dataclient.mwapi.MwPostResponse; import org.wikipedia.dataclient.mwapi.MwPostResponse;
import org.wikipedia.wikidata.DataValue;
import org.wikipedia.wikidata.DataValue.ValueString;
import org.wikipedia.wikidata.EditClaim; import org.wikipedia.wikidata.EditClaim;
import org.wikipedia.wikidata.Snak_partial;
import org.wikipedia.wikidata.Statement_partial;
import org.wikipedia.wikidata.WikiBaseMonolingualTextValue;
import timber.log.Timber; import timber.log.Timber;
/** /**
* This class is meant to handle the Wikidata edits made through the app * This class is meant to handle the Wikidata edits made through the app It will talk with MediaWiki
* It will talk with MediaWiki Apis to make the necessary calls, log the edits and fire listeners * Apis to make the necessary calls, log the edits and fire listeners on successful edits
* on successful edits
*/ */
@Singleton @Singleton
public class WikidataEditService { public class WikidataEditService {
private static final String COMMONS_APP_TAG = "wikimedia-commons-app"; public static final String COMMONS_APP_TAG = "wikimedia-commons-app";
private static final String COMMONS_APP_EDIT_REASON = "Add tag for edits made using Android Commons app";
private final Context context; private final Context context;
private final WikidataEditListener wikidataEditListener; private final WikidataEditListener wikidataEditListener;
private final JsonKvStore directKvStore; private final JsonKvStore directKvStore;
private final WikiBaseClient wikiBaseClient; private final WikiBaseClient wikiBaseClient;
private final WikidataClient wikidataClient; private final WikidataClient wikidataClient;
private final Gson gson; private final Gson gson;
@Inject @Inject
public WikidataEditService(final Context context, public WikidataEditService(final Context context,
final WikidataEditListener wikidataEditListener, final WikidataEditListener wikidataEditListener,
@Named("default_preferences") final JsonKvStore directKvStore, @Named("default_preferences") final JsonKvStore directKvStore,
final WikiBaseClient wikiBaseClient, final WikiBaseClient wikiBaseClient,
final WikidataClient wikidataClient, final Gson gson) { final WikidataClient wikidataClient, final Gson gson) {
this.context = context; this.context = context;
this.wikidataEditListener = wikidataEditListener; this.wikidataEditListener = wikidataEditListener;
this.directKvStore = directKvStore; this.directKvStore = directKvStore;
this.wikiBaseClient = wikiBaseClient; this.wikiBaseClient = wikiBaseClient;
this.wikidataClient = wikidataClient; this.wikidataClient = wikidataClient;
this.gson = gson; this.gson = gson;
} }
/** /**
* Edits the wikibase entity by adding DEPICTS property. * Edits the wikibase entity by adding DEPICTS property. Adding DEPICTS property requires call to
* Adding DEPICTS property requires call to the wikibase API to set tag against the entity. * the wikibase API to set tag against the entity.
*/ */
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private Observable<Boolean> addDepictsProperty(final String fileEntityId, private Observable<Boolean> addDepictsProperty(final String fileEntityId,
@ -70,7 +79,7 @@ public class WikidataEditService {
final EditClaim data = editClaim( final EditClaim data = editClaim(
ConfigUtils.isBetaFlavour() ? "Q10" // Wikipedia:Sandbox (Q10) ConfigUtils.isBetaFlavour() ? "Q10" // Wikipedia:Sandbox (Q10)
: depictedItem.getId() : depictedItem.getId()
); );
return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, gson.toJson(data)) return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, gson.toJson(data))
@ -81,44 +90,46 @@ public class WikidataEditService {
Timber.d("Unable to set DEPICTS property for %s", fileEntityId); Timber.d("Unable to set DEPICTS property for %s", fileEntityId);
} }
}) })
.doOnError( throwable -> { .doOnError(throwable -> {
Timber.e(throwable, "Error occurred while setting DEPICTS property"); Timber.e(throwable, "Error occurred while setting DEPICTS property");
ViewUtil.showLongToast(context, throwable.toString()); ViewUtil.showLongToast(context, throwable.toString());
}) })
.subscribeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
private EditClaim editClaim(final String entityId) { private EditClaim editClaim(final String entityId) {
return EditClaim.from(entityId, WikidataProperties.DEPICTS.getPropertyName()); return EditClaim.from(entityId, WikidataProperties.DEPICTS.getPropertyName());
} }
/** /**
* Show a success toast when the edit is made successfully * Show a success toast when the edit is made successfully
*/ */
private void showSuccessToast(final String wikiItemName) { private void showSuccessToast(final String wikiItemName) {
final String successStringTemplate = context.getString(R.string.successful_wikidata_edit); final String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
final String successMessage = String.format(Locale.getDefault(), successStringTemplate, wikiItemName); final String successMessage = String
ViewUtil.showLongToast(context, successMessage); .format(Locale.getDefault(), successStringTemplate, wikiItemName);
} ViewUtil.showLongToast(context, successMessage);
}
/** /**
* Adds label to Wikidata using the fileEntityId and the edit token, obtained from csrfTokenClient * Adds label to Wikidata using the fileEntityId and the edit token, obtained from
* * csrfTokenClient
* @param fileEntityId *
* @return * @param fileEntityId
*/ * @return
*/
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private Observable<Boolean> addCaption(final long fileEntityId, final String languageCode, private Observable<Boolean> addCaption(final long fileEntityId, final String languageCode,
final String captionValue) { final String captionValue) {
return wikiBaseClient.addLabelstoWikidata(fileEntityId, languageCode, captionValue) return wikiBaseClient.addLabelstoWikidata(fileEntityId, languageCode, captionValue)
.doOnNext(mwPostResponse -> onAddCaptionResponse(fileEntityId, mwPostResponse) ) .doOnNext(mwPostResponse -> onAddCaptionResponse(fileEntityId, mwPostResponse))
.doOnError(throwable -> { .doOnError(throwable -> {
Timber.e(throwable, "Error occurred while setting Captions"); Timber.e(throwable, "Error occurred while setting Captions");
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure)); ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
}) })
.map(mwPostResponse -> mwPostResponse != null); .map(mwPostResponse -> mwPostResponse != null);
} }
private void onAddCaptionResponse(Long fileEntityId, MwPostResponse response) { private void onAddCaptionResponse(Long fileEntityId, MwPostResponse response) {
if (response != null) { if (response != null) {
@ -128,29 +139,41 @@ public class WikidataEditService {
} }
} }
public void createImageClaim(@Nullable final WikidataPlace wikidataPlace, final UploadResult imageUpload) { public void createClaim(@Nullable final WikidataPlace wikidataPlace, final String fileName, final
Map<String, String> captions) {
if (!(directKvStore.getBoolean("Picture_Has_Correct_Location", true))) { if (!(directKvStore.getBoolean("Picture_Has_Correct_Location", true))) {
Timber.d("Image location and nearby place location mismatched, so Wikidata item won't be edited"); Timber
.d("Image location and nearby place location mismatched, so Wikidata item won't be edited");
return; return;
} }
editWikidataImageProperty(wikidataPlace, imageUpload); addImageAndMediaLegends(wikidataPlace, fileName, captions);
} }
@SuppressLint("CheckResult") public void addImageAndMediaLegends(final WikidataItem wikidataItem, final String fileName,
private void editWikidataImageProperty(final WikidataItem wikidataItem, final UploadResult imageUpload) { final Map<String, String> captions) {
wikidataClient.createImageClaim(wikidataItem, String.format("\"%s\"", imageUpload.getFilename())) final Snak_partial p18 = new Snak_partial("value", WikidataProperties.IMAGE.getPropertyName(),
.flatMap(revisionId -> { new ValueString(fileName.replace("File:", "")));
if (revisionId != -1) {
return wikidataClient.addEditTag(revisionId, COMMONS_APP_TAG, COMMONS_APP_EDIT_REASON); final List<Snak_partial> snaks = new ArrayList<>();
} for (final Map.Entry<String, String> entry : captions.entrySet()) {
throw new RuntimeException("Unable to edit wikidata item"); snaks.add(new Snak_partial("value",
}) WikidataProperties.MEDIA_LEGENDS.getPropertyName(), new DataValue.MonoLingualText(
.subscribeOn(Schedulers.io()) new WikiBaseMonolingualTextValue(entry.getValue(), entry.getKey()))));
}
final String id = wikidataItem.getId() + "$" + UUID.randomUUID().toString();
final Statement_partial claim = new Statement_partial(p18, "statement", "normal", id,
Collections.singletonMap(WikidataProperties.MEDIA_LEGENDS.getPropertyName(), snaks),
Arrays.asList(WikidataProperties.MEDIA_LEGENDS.getPropertyName()));
wikidataClient.setClaim(claim, COMMONS_APP_TAG).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(revisionId -> handleImageClaimResult(wikidataItem, String.valueOf(revisionId)), throwable -> { .subscribe(revisionId -> handleImageClaimResult(wikidataItem, String.valueOf(revisionId)),
Timber.e(throwable, "Error occurred while making claim"); throwable -> {
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure)); Timber.e(throwable, "Error occurred while making claim");
}); ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
});
;
} }
private void handleImageClaimResult(final WikidataItem wikidataItem, final String revisionId) { private void handleImageClaimResult(final WikidataItem wikidataItem, final String revisionId) {
@ -185,9 +208,9 @@ public class WikidataEditService {
} }
} }
).subscribe( ).subscribe(
success -> Timber.d("edit response: %s", success), success -> Timber.d("edit response: %s", success),
throwable -> Timber.e(throwable, "posting edits failed") throwable -> Timber.e(throwable, "posting edits failed")
); );
} }
private Observable<Boolean> captionEdits(Contribution contribution, Long fileEntityId) { private Observable<Boolean> captionEdits(Contribution contribution, Long fileEntityId) {
@ -202,6 +225,6 @@ public class WikidataEditService {
depictedItems.add(wikidataPlace); depictedItems.add(wikidataPlace);
} }
return Observable.fromIterable(depictedItems) return Observable.fromIterable(depictedItems)
.concatMap( wikidataItem -> addDepictsProperty(fileEntityId.toString(), wikidataItem)); .concatMap(wikidataItem -> addDepictsProperty(fileEntityId.toString(), wikidataItem));
} }
} }

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.wikidata;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.gson.JsonObject;
import org.wikipedia.dataclient.mwapi.MwQueryResponse; import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse; import fr.free.nrw.commons.wikidata.model.AddEditTagResponse;
@ -20,35 +21,21 @@ import static org.wikipedia.dataclient.Service.MW_API_PREFIX;
public interface WikidataInterface { public interface WikidataInterface {
/** /**
* Wikidata create claim API. Posts a new claim for the given entity ID * Get edit token for wikidata wiki site
*/ */
@Headers("Cache-Control: no-cache") @Headers("Cache-Control: no-cache")
@POST("w/api.php?format=json&errorformat=plaintext&action=wbcreateclaim&errorlang=uselang") @GET(MW_API_PREFIX + "action=query&meta=tokens&type=csrf")
@Multipart @NonNull
Observable<WbCreateClaimResponse> postCreateClaim(@NonNull @Part("entity") RequestBody entity, Observable<MwQueryResponse> getCsrfToken();
@NonNull @Part("snaktype") RequestBody snakType,
@NonNull @Part("property") RequestBody property,
@NonNull @Part("value") RequestBody value,
@NonNull @Part("uselang") RequestBody useLang,
@NonNull @Part("token") RequestBody token);
/** /**
* Add edit tag and reason for any revision * Wikidata create claim API. Posts a new claim for the given entity ID
*/ */
@Headers("Cache-Control: no-cache") @Headers("Cache-Control: no-cache")
@POST(MW_API_PREFIX + "action=tag") @POST("w/api.php?format=json&action=wbsetclaim")
@FormUrlEncoded @FormUrlEncoded
Observable<AddEditTagResponse> addEditTag(@NonNull @Field("revid") String revId, Observable<WbCreateClaimResponse> postSetClaim(@NonNull @Field("claim") String request,
@NonNull @Field("add") String tagName, @NonNull @Field("tags") String tags,
@NonNull @Field("reason") String reason, @NonNull @Field("token") String token);
@NonNull @Field("token") String token);
/**
* Get edit token for wikidata wiki site
*/
@Headers("Cache-Control: no-cache")
@GET(MW_API_PREFIX + "action=query&meta=tokens&type=csrf")
@NonNull
Observable<MwQueryResponse> getCsrfToken();
} }

View file

@ -6,5 +6,6 @@ enum class WikidataProperties(val propertyName: String) {
IMAGE("P18"), IMAGE("P18"),
DEPICTS(BuildConfig.DEPICTS_PROPERTY), DEPICTS(BuildConfig.DEPICTS_PROPERTY),
COMMONS_CATEGORY("P373"), COMMONS_CATEGORY("P373"),
INSTANCE_OF("P31"); INSTANCE_OF("P31"),
MEDIA_LEGENDS("P2096");
} }

View file

@ -1,7 +1,9 @@
package fr.free.nrw.commons.wikidata package fr.free.nrw.commons.wikidata
import com.nhaarman.mockitokotlin2.mock import com.google.gson.Gson
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.wikidata.model.PageInfo
import fr.free.nrw.commons.wikidata.model.WbCreateClaimResponse
import io.reactivex.Observable import io.reactivex.Observable
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -14,12 +16,16 @@ import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.wikipedia.dataclient.mwapi.MwQueryResponse import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.dataclient.mwapi.MwQueryResult import org.wikipedia.dataclient.mwapi.MwQueryResult
import org.wikipedia.wikidata.Statement_partial
class WikidataClientTest { class WikidataClientTest {
@Mock @Mock
internal var wikidataInterface: WikidataInterface? = null internal var wikidataInterface: WikidataInterface? = null
@Mock
internal var gson: Gson? = null
@InjectMocks @InjectMocks
var wikidataClient: WikidataClient? = null var wikidataClient: WikidataClient? = null
@ -35,26 +41,18 @@ class WikidataClientTest {
.thenReturn(Observable.just(mwQueryResponse)) .thenReturn(Observable.just(mwQueryResponse))
} }
@Test
fun createClaim() {
`when`(
wikidataInterface!!.postCreateClaim(
any(),
any(),
any(),
any(),
any(),
any()
)
)
.thenReturn(Observable.just(mock()))
wikidataClient!!.createImageClaim(mock(), "test.jpg")
}
@Test @Test
fun addEditTag() { fun addEditTag() {
`when`(wikidataInterface!!.addEditTag(anyString(), anyString(), anyString(), anyString())) val response = mock(WbCreateClaimResponse::class.java)
.thenReturn(Observable.just(mock(AddEditTagResponse::class.java))) val pageInfo = mock(PageInfo::class.java)
wikidataClient!!.addEditTag(1L, "test", "test") whenever(pageInfo.lastrevid).thenReturn(1)
whenever(response.pageinfo).thenReturn(pageInfo)
`when`(wikidataInterface!!.postSetClaim(anyString(), anyString(), anyString()))
.thenReturn(Observable.just(response))
whenever(gson!!.toJson(any(Statement_partial::class.java))).thenReturn("claim")
val request = mock(Statement_partial::class.java)
val claim = wikidataClient!!.setClaim(request, "test").test()
.assertValue(1L)
} }
} }

View file

@ -1,13 +1,13 @@
package fr.free.nrw.commons.wikidata package fr.free.nrw.commons.wikidata
import android.content.Context import android.content.Context
import com.google.gson.Gson
import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verifyZeroInteractions import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import com.nhaarman.mockitokotlin2.whenever import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.upload.UploadResult import fr.free.nrw.commons.upload.UploadResult
import fr.free.nrw.commons.upload.WikidataPlace import fr.free.nrw.commons.upload.WikidataPlace
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse
import io.reactivex.Observable import io.reactivex.Observable
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -15,7 +15,6 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyString import org.mockito.ArgumentMatchers.anyString
import org.mockito.InjectMocks import org.mockito.InjectMocks
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
class WikidataEditServiceTest { class WikidataEditServiceTest {
@ -31,6 +30,9 @@ class WikidataEditServiceTest {
@Mock @Mock
internal lateinit var wikibaseClient: WikiBaseClient internal lateinit var wikibaseClient: WikiBaseClient
@Mock
internal lateinit var gson: Gson
@InjectMocks @InjectMocks
lateinit var wikidataEditService: WikidataEditService lateinit var wikidataEditService: WikidataEditService
@ -44,7 +46,7 @@ class WikidataEditServiceTest {
fun noClaimsWhenLocationIsNotCorrect() { fun noClaimsWhenLocationIsNotCorrect() {
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true)) whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
.thenReturn(false) .thenReturn(false)
wikidataEditService.createImageClaim(mock(), mock()) wikidataEditService.createClaim(mock(), "Test.jpg", hashMapOf())
verifyZeroInteractions(wikidataClient) verifyZeroInteractions(wikidataClient)
} }
@ -52,15 +54,16 @@ class WikidataEditServiceTest {
fun createImageClaim() { fun createImageClaim() {
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true)) whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
.thenReturn(true) .thenReturn(true)
whenever(wikidataClient.createImageClaim(any(), any()))
.thenReturn(Observable.just(1L))
whenever(wikidataClient.addEditTag(anyLong(), anyString(), anyString()))
.thenReturn(Observable.just(mock(AddEditTagResponse::class.java)))
whenever(wikibaseClient.getFileEntityId(any())).thenReturn(Observable.just(1L)) whenever(wikibaseClient.getFileEntityId(any())).thenReturn(Observable.just(1L))
val wikidataPlace:WikidataPlace = mock() whenever(wikidataClient.setClaim(any(), anyString()))
.thenReturn(Observable.just(1L))
val wikidataPlace: WikidataPlace = mock()
val uploadResult = mock<UploadResult>() val uploadResult = mock<UploadResult>()
whenever(uploadResult.filename).thenReturn("file") whenever(uploadResult.filename).thenReturn("file")
wikidataEditService.createImageClaim(wikidataPlace, uploadResult) wikidataEditService.createClaim(
verify(wikidataClient, times(1)).createImageClaim(wikidataPlace, """"file"""") wikidataPlace,
uploadResult.filename,
hashMapOf<String, String>()
)
} }
} }

View file

@ -12,7 +12,7 @@ sealed class DataValue(val type: String) {
.registerSubtype(GlobeCoordinate_partial::class.java, GlobeCoordinate_partial.TYPE) .registerSubtype(GlobeCoordinate_partial::class.java, GlobeCoordinate_partial.TYPE)
.registerSubtype(Time_partial::class.java, Time_partial.TYPE) .registerSubtype(Time_partial::class.java, Time_partial.TYPE)
.registerSubtype(Quantity_partial::class.java, Quantity_partial.TYPE) .registerSubtype(Quantity_partial::class.java, Quantity_partial.TYPE)
.registerSubtype(MonoLingualText_partial::class.java, MonoLingualText_partial.TYPE) .registerSubtype(MonoLingualText::class.java, MonoLingualText.TYPE)
} }
// "value": { // "value": {
@ -87,7 +87,7 @@ sealed class DataValue(val type: String) {
// "language": "ko" // "language": "ko"
// } // }
// } // }
class MonoLingualText_partial() : DataValue(TYPE) { class MonoLingualText(val value: WikiBaseMonolingualTextValue) : DataValue(TYPE) {
companion object { companion object {
const val TYPE = "monolingualtext" const val TYPE = "monolingualtext"
} }

View file

@ -21,5 +21,8 @@ import com.google.gson.annotations.SerializedName
data class Statement_partial( data class Statement_partial(
@SerializedName("mainsnak") val mainSnak: Snak_partial, @SerializedName("mainsnak") val mainSnak: Snak_partial,
val type: String, val type: String,
val rank: String val rank: String,
val id: String? = null,
val qualifiers: Map<String, List<Snak_partial>> = mapOf(),
@SerializedName("qualifiers-order") val qualifiersOrder: List<String> = listOf()
) )

View file

@ -0,0 +1,13 @@
package org.wikipedia.wikidata
import com.google.gson.annotations.SerializedName
/*"value": {
"type": "monolingualtext",
"value": {
"text": "some value",
"language": "en"
}
}*/
data class WikiBaseMonolingualTextValue(val text: String, val language: String)