#3529 Captions/depictions are not saved to Commons (#3574)

* #3529 Captions/depictions are not saved to Commons - make copy of list of depictionEntityIds - uncomment editBaseDepictsProperty - refactor upload related classes

* #3529 Captions/depictions are not saved to Commons - fix wrong ArrayList usage

* #3529 Captions/depictions are not saved to Commons - fix test
This commit is contained in:
Seán Mac Gillicuddy 2020-03-25 10:42:29 +00:00 committed by GitHub
parent 587d97716a
commit 23b8c2e659
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 360 additions and 504 deletions

View file

@ -3,18 +3,12 @@ package fr.free.nrw.commons;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import org.apache.commons.lang3.StringUtils;
import org.wikipedia.dataclient.mwapi.MwQueryPage;
import org.wikipedia.gallery.ExtMetadata;
import org.wikipedia.gallery.ImageInfo;
import org.wikipedia.page.PageTitle;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
@ -23,10 +17,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
import org.apache.commons.lang3.StringUtils;
import org.wikipedia.dataclient.mwapi.MwQueryPage;
import org.wikipedia.gallery.ExtMetadata;
import org.wikipedia.gallery.ImageInfo;
import org.wikipedia.page.PageTitle;
@Entity
public class Media implements Parcelable {
@ -90,7 +85,7 @@ public class Media implements Parcelable {
* Ex: key = "en", value: "<caption in short in English>"
* key = "de" , value: "<caption in german>"
*/
public HashMap<String, String> captions;
public Map<String, String> captions;
public HashMap<String, String> tags = new HashMap<>();
@Nullable public LatLng coordinates;
@ -126,7 +121,7 @@ public class Media implements Parcelable {
* @param dateUploaded Media date uploaded
* @param creator Media creator
*/
public Media(Uri localUri, String imageUrl, String filename, HashMap<String, String> captions, String description,
public Media(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description,
long dataLength, Date dateCreated, Date dateUploaded, String creator) {
this();
this.localUri = localUri;
@ -395,7 +390,7 @@ public class Media implements Parcelable {
*
* returns list of captions stored in hashmap
*/
public HashMap<String, String> getCaptions() {
public Map<String, String> getCaptions() {
return captions;
}

View file

@ -1,29 +1,25 @@
package fr.free.nrw.commons.contributions;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
import androidx.annotation.NonNull;
import androidx.annotation.StringDef;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import org.apache.commons.lang3.StringUtils;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.filepicker.UploadableFile;
import fr.free.nrw.commons.settings.Prefs;
import fr.free.nrw.commons.utils.ConfigUtils;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
@Entity(tableName = "contribution")
public class Contribution extends Media {
@ -83,7 +79,7 @@ public class Contribution extends Media {
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated,
int state, long dataLength, Date dateUploaded, long transferred,
String source, HashMap<String, String> captions, String description, String creator, boolean isMultiple,
String source, Map<String, String> captions, String description, String creator, boolean isMultiple,
int width, int height, String license) {
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
this.contentUri = contentUri;
@ -97,7 +93,7 @@ public class Contribution extends Media {
this.dateCreatedSource = "";
}
public Contribution(Uri localUri, String imageUrl, String filename, HashMap<String, String> captions, String description, long dataLength,
public Contribution(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description, long dataLength,
Date dateCreated, Date dateUploaded, String creator, String editSummary, ArrayList<String> depictionsEntityIds, String decimalCoords) {
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
this.decimalCoords = decimalCoords;
@ -106,7 +102,7 @@ public class Contribution extends Media {
this.depictionsEntityIds = depictionsEntityIds;
}
public Contribution(Uri localUri, String imageUrl, String filename, HashMap<String, String> captions, String description, long dataLength,
public Contribution(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description, long dataLength,
Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords, int state) {
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
this.decimalCoords = decimalCoords;

View file

@ -1,24 +1,8 @@
package fr.free.nrw.commons.di;
import android.content.Context;
import androidx.annotation.NonNull;
import com.google.gson.Gson;
import org.wikipedia.csrf.CsrfTokenClient;
import org.wikipedia.dataclient.Service;
import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.json.GsonUtil;
import org.wikipedia.login.LoginClient;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import fr.free.nrw.commons.BuildConfig;
@ -32,14 +16,25 @@ import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.mwapi.UserInterface;
import fr.free.nrw.commons.review.ReviewInterface;
import fr.free.nrw.commons.upload.UploadInterface;
import fr.free.nrw.commons.wikidata.WikidataInterface;
import fr.free.nrw.commons.upload.WikiBaseInterface;
import fr.free.nrw.commons.upload.depicts.DepictsInterface;
import fr.free.nrw.commons.upload.mediaDetails.CaptionInterface;
import fr.free.nrw.commons.wikidata.WikidataInterface;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import okhttp3.Cache;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.logging.HttpLoggingInterceptor.Level;
import org.wikipedia.csrf.CsrfTokenClient;
import org.wikipedia.dataclient.Service;
import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.json.GsonUtil;
import org.wikipedia.login.LoginClient;
import timber.log.Timber;
@Module
@ -76,7 +71,7 @@ public class NetworkingModule {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(message -> {
Timber.tag("OkHttp").v(message);
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpLoggingInterceptor.level(BuildConfig.DEBUG ? Level.BODY: HttpLoggingInterceptor.Level.BASIC);
return httpLoggingInterceptor;
}

View file

@ -13,17 +13,8 @@ import android.net.Uri;
import android.os.IBinder;
import android.provider.MediaStore;
import android.text.TextUtils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import javax.inject.Inject;
import javax.inject.Singleton;
import fr.free.nrw.commons.HandlerService;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.Contribution;
@ -34,23 +25,26 @@ import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import javax.inject.Inject;
import javax.inject.Singleton;
import timber.log.Timber;
@Singleton
public class UploadController {
private UploadService uploadService;
private SessionManager sessionManager;
private Context context;
private JsonKvStore store;
public interface ContributionUploadProgress {
void onUploadStarted(Contribution contribution);
}
private final SessionManager sessionManager;
private final Context context;
private final JsonKvStore store;
@Inject
public UploadController(SessionManager sessionManager,
Context context,
JsonKvStore store) {
public UploadController(final SessionManager sessionManager,
final Context context,
final JsonKvStore store) {
this.sessionManager = sessionManager;
this.context = context;
this.store = store;
@ -59,13 +53,13 @@ public class UploadController {
private boolean isUploadServiceConnected;
public ServiceConnection uploadServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
public void onServiceConnected(final ComponentName componentName, final IBinder binder) {
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder).getService();
isUploadServiceConnected = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
public void onServiceDisconnected(final ComponentName componentName) {
// this should never happen
isUploadServiceConnected = false;
Timber.e(new RuntimeException("UploadService died but the rest of the process did not!"));
@ -76,7 +70,7 @@ public class UploadController {
* Prepares the upload service.
*/
public void prepareService() {
Intent uploadServiceIntent = new Intent(context, UploadService.class);
final Intent uploadServiceIntent = new Intent(context, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
context.startService(uploadServiceIntent);
context.bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
@ -96,28 +90,18 @@ public class UploadController {
*
* @param contribution the contribution object
*/
public void startUpload(Contribution contribution) {
startUpload(contribution, c -> {});
}
/**
* Starts a new upload task.
*
* @param contribution the contribution object
* @param onComplete the progress tracker
*/
@SuppressLint("StaticFieldLeak")
private void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
public void startUpload(final Contribution contribution) {
//Set creator, desc, and license
// If author name is enabled and set, use it
if (store.getBoolean("useAuthorName", false)) {
String authorName = store.getString("authorName", "");
final String authorName = store.getString("authorName", "");
contribution.setCreator(authorName);
}
if (TextUtils.isEmpty(contribution.getCreator())) {
Account currentAccount = sessionManager.getCurrentAccount();
final Account currentAccount = sessionManager.getCurrentAccount();
if (currentAccount == null) {
Timber.d("Current account is null");
ViewUtil.showLongToast(context, context.getString(R.string.user_not_logged_in));
@ -135,23 +119,23 @@ public class UploadController {
contribution.setCaption("");
}
String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
final String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
contribution.setLicense(license);
uploadTask(contribution, onComplete);
uploadTask(contribution);
}
/**
* Initiates the upload task
* @param contribution
* @param onComplete
* @return
*/
private Disposable uploadTask(Contribution contribution, ContributionUploadProgress onComplete) {
return Single.fromCallable(() -> makeUpload(contribution))
private Disposable uploadTask(final Contribution contribution) {
return Single.just(contribution)
.map(this::buildUpload)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(finalContribution -> onUploadCompleted(finalContribution, onComplete));
.subscribe(this::upload);
}
/**
@ -159,71 +143,76 @@ public class UploadController {
* @param contribution
* @return
*/
private Contribution makeUpload(Contribution contribution) {
long length;
ContentResolver contentResolver = context.getContentResolver();
private Contribution buildUpload(final Contribution contribution) {
final ContentResolver contentResolver = context.getContentResolver();
contribution.setDataLength(resolveDataLength(contentResolver, contribution));
final String mimeType = resolveMimeType(contentResolver, contribution);
if (mimeType != null) {
Timber.d("MimeType is: %s", mimeType);
contribution.setTag("mimeType", mimeType);
if(mimeType.startsWith("image/") && contribution.getDateCreated() == null){
contribution.setDateCreated(resolveDateTakenOrNow(contentResolver, contribution));
}
}
return contribution;
}
private String resolveMimeType(final ContentResolver contentResolver, final Contribution contribution) {
final String mimeType = (String) contribution.getTag("mimeType");
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
return contentResolver.getType(contribution.getLocalUri());
}
return mimeType;
}
private long resolveDataLength(final ContentResolver contentResolver, final Media contribution) {
try {
if (contribution.getDataLength() <= 0) {
Timber.d("UploadController/doInBackground, contribution.getLocalUri():%s", contribution.getLocalUri());
AssetFileDescriptor assetFileDescriptor = contentResolver
.openAssetFileDescriptor(Uri.fromFile(new File(contribution.getLocalUri().getPath())), "r");
final AssetFileDescriptor assetFileDescriptor = contentResolver
.openAssetFileDescriptor(Uri.fromFile(new File(contribution.getLocalUri().getPath())), "r");
if (assetFileDescriptor != null) {
length = assetFileDescriptor.getLength();
if (length == -1) {
// Let us find out the long way!
length = countBytes(contentResolver
.openInputStream(contribution.getLocalUri()));
}
contribution.setDataLength(length);
final long length = assetFileDescriptor.getLength();
return length != -1 ? length
: countBytes(contentResolver.openInputStream(contribution.getLocalUri()));
}
}
} catch (IOException | NullPointerException | SecurityException e) {
} catch (final IOException | NullPointerException | SecurityException e) {
Timber.e(e, "Exception occurred while uploading image");
}
return contribution.getDataLength();
}
String mimeType = (String) contribution.getTag("mimeType");
boolean imagePrefix = false;
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
mimeType = contentResolver.getType(contribution.getLocalUri());
}
if (mimeType != null) {
contribution.setTag("mimeType", mimeType);
imagePrefix = mimeType.startsWith("image/");
Timber.d("MimeType is: %s", mimeType);
}
if (imagePrefix && contribution.getDateCreated() == null) {
Timber.d("local uri %s", contribution.getLocalUri());
Cursor cursor = contentResolver.query(contribution.getLocalUri(),
new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null);
private Date resolveDateTakenOrNow(final ContentResolver contentResolver, final Media contribution) {
Timber.d("local uri %s", contribution.getLocalUri());
try(final Cursor cursor = dateTakenCursor(contentResolver, contribution)) {
if (cursor != null && cursor.getCount() != 0 && cursor.getColumnCount() != 0) {
cursor.moveToFirst();
Date dateCreated = new Date(cursor.getLong(0));
Date epochStart = new Date(0);
if (dateCreated.equals(epochStart) || dateCreated.before(epochStart)) {
// If date is incorrect (1st second of unix time) then set it to the current date
dateCreated = new Date();
final Date dateCreated = new Date(cursor.getLong(0));
if (dateCreated.after(new Date(0))) {
return dateCreated;
}
contribution.setDateCreated(dateCreated);
cursor.close();
} else {
contribution.setDateCreated(new Date());
}
return new Date();
}
return contribution;
}
private Cursor dateTakenCursor(final ContentResolver contentResolver, final Media contribution) {
return contentResolver.query(contribution.getLocalUri(),
new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null);
}
/**
* When the contribution object is completely formed, the item is queued to the upload service
* @param contribution
* @param onComplete
*/
private void onUploadCompleted(Contribution contribution, ContributionUploadProgress onComplete) {
private void upload(final Contribution contribution) {
//Starts the upload. If commented out, user can proceed to next Fragment but upload doesn't happen
uploadService.queue(UploadService.ACTION_UPLOAD_FILE, contribution);
onComplete.onUploadStarted(contribution);
}
@ -234,9 +223,9 @@ public class UploadController {
* @return the number of bytes in {@code stream}
* @throws IOException if an I/O error occurs
*/
private long countBytes(InputStream stream) throws IOException {
private long countBytes(final InputStream stream) throws IOException {
long count = 0;
BufferedInputStream bis = new BufferedInputStream(stream);
final BufferedInputStream bis = new BufferedInputStream(stream);
while (bis.read() != -1) {
count++;
}

View file

@ -1,117 +0,0 @@
package fr.free.nrw.commons.upload;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import timber.log.Timber;
/**
* Holds a description of an item being uploaded by {@link UploadActivity}
*/
public class UploadMediaDetail {
private String languageCode;
private String descriptionText;
public String captionText;
private int selectedLanguageIndex = -1;
private boolean isManuallyAdded=false;
/**
* Formatting captions to the Wikibase format for sending labels
* @param uploadMediaDetails list of media Details
*/
public static HashMap<String, String> formatCaptions(List<UploadMediaDetail> uploadMediaDetails) {
HashMap<String, String> caption = new HashMap<>();
for (UploadMediaDetail uploadMediaDetail : uploadMediaDetails) {
caption.put(uploadMediaDetail.getLanguageCode(),uploadMediaDetail.getCaptionText());
}
return caption;
}
public String getCaptionText() {
return captionText;
}
public void setCaptionText(String captionText) {
this.captionText = captionText;
}
/**
* @return The language code ie. "en" or "fr"
*/
String getLanguageCode() {
return languageCode;
}
/**
* @param languageCode The language code ie. "en" or "fr"
*/
public void setLanguageCode(String languageCode) {
this.languageCode = languageCode;
}
String getDescriptionText() {
return descriptionText;
}
public void setDescriptionText(String descriptionText) {
this.descriptionText = descriptionText;
}
/**
* @return the index of the language selected in a spinner with {@link SpinnerLanguagesAdapter}
*/
int getSelectedLanguageIndex() {
return selectedLanguageIndex;
}
/**
* @param selectedLanguageIndex the index of the language selected in a spinner with {@link SpinnerLanguagesAdapter}
*/
void setSelectedLanguageIndex(int selectedLanguageIndex) {
this.selectedLanguageIndex = selectedLanguageIndex;
}
/**
* returns if the description was added manually (by the user, or we have added it programaticallly)
* @return
*/
public boolean isManuallyAdded() {
return isManuallyAdded;
}
/**
* sets to true if the description was manually added by the user
* @param manuallyAdded
*/
public void setManuallyAdded(boolean manuallyAdded) {
isManuallyAdded = manuallyAdded;
}
/**
* Formats the list of descriptions into the format Commons requires for uploads.
*
* @param descriptions the list of descriptions, description is ignored if text is null.
* @return a string with the pattern of {{en|1=descriptionText}}
*/
static String formatList(List<UploadMediaDetail> descriptions) {
StringBuilder descListString = new StringBuilder();
for (UploadMediaDetail description : descriptions) {
if (!description.isEmpty()) {
String individualDescription = String.format("{{%s|1=%s}}", description.getLanguageCode(),
description.getDescriptionText());
descListString.append(individualDescription);
}
}
return descListString.toString();
}
public boolean isEmpty() {
return descriptionText == null || descriptionText.isEmpty();
}
}

View file

@ -0,0 +1,66 @@
package fr.free.nrw.commons.upload
import fr.free.nrw.commons.nearby.Place
import java.util.*
/**
* Holds a description of an item being uploaded by [UploadActivity]
*/
data class UploadMediaDetail constructor(
/**
* @return The language code ie. "en" or "fr"
*/
/**
* @param languageCode The language code ie. "en" or "fr"
*/
var languageCode: String? = null,
var descriptionText: String = "",
var captionText: String = ""
) {
constructor(place: Place) : this(
Locale.getDefault().language,
place.longDescription,
place.name
)
/**
* @return the index of the language selected in a spinner with [SpinnerLanguagesAdapter]
*/
/**
* @param selectedLanguageIndex the index of the language selected in a spinner with [SpinnerLanguagesAdapter]
*/
var selectedLanguageIndex: Int = -1
/**
* returns if the description was added manually (by the user, or we have added it programaticallly)
* @return
*/
/**
* sets to true if the description was manually added by the user
* @param manuallyAdded
*/
var isManuallyAdded: Boolean = false
companion object {
/**
* Formatting captions to the Wikibase format for sending labels
* @param uploadMediaDetails list of media Details
*/
@JvmStatic
fun formatCaptions(uploadMediaDetails: List<UploadMediaDetail>) =
uploadMediaDetails.associate { it.languageCode to it.captionText }
/**
* Formats the list of descriptions into the format Commons requires for uploads.
*
* @param descriptions the list of descriptions, description is ignored if text is null.
* @return a string with the pattern of {{en|1=descriptionText}}
*/
@JvmStatic
fun formatList(descriptions: List<UploadMediaDetail>) =
descriptions.joinToString {
if (it.descriptionText.isNotEmpty())
"{{${it.languageCode}|1=${it.descriptionText}}}"
else
""
}
}
}

View file

@ -2,9 +2,7 @@ package fr.free.nrw.commons.upload;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@ -107,36 +105,11 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
public void init(int position) {
UploadMediaDetail uploadMediaDetail = uploadMediaDetails.get(position);
Timber.d("UploadMediaDetail is " + uploadMediaDetail);
if (!TextUtils.isEmpty(uploadMediaDetail.getCaptionText())) {
captionItemEditText.setText(uploadMediaDetail.getCaptionText());
} else {
captionItemEditText.setText("");
}
captionItemEditText.setText(uploadMediaDetail.getCaptionText());
descItemEditText.setText(uploadMediaDetail.getDescriptionText());
if (!TextUtils.isEmpty(uploadMediaDetail.getDescriptionText())) {
descItemEditText.setText(uploadMediaDetail.getDescriptionText());
} else {
descItemEditText.setText("");
}
captionItemEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (s.length() != 0) {
eventListener.onEvent(true);
} else eventListener.onEvent(false);
}
});
captionItemEditText.addTextChangedListener(new AbstractTextWatcher(
value -> eventListener.onEvent(value.length() != 0)) );
if (position == 0) {
captionItemEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, getInfoIcon(),
@ -174,13 +147,11 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
}
captionItemEditText.addTextChangedListener(new AbstractTextWatcher(
captionText -> uploadMediaDetails.get(position)
.setCaptionText(captionText)));
captionText -> uploadMediaDetails.get(position).setCaptionText(captionText)));
initLanguageSpinner(position, uploadMediaDetail);
descItemEditText.addTextChangedListener(new AbstractTextWatcher(
descriptionText -> uploadMediaDetails.get(position)
.setDescriptionText(descriptionText)));
descriptionText -> uploadMediaDetails.get(position).setDescriptionText(descriptionText)));
initLanguageSpinner(position, uploadMediaDetail);
//If the description was manually added by the user, it deserves focus, if not, let the user decide

View file

@ -7,7 +7,6 @@ import androidx.annotation.Nullable;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper;
import fr.free.nrw.commons.filepicker.UploadableFile;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.nearby.Place;
@ -18,15 +17,17 @@ import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.subjects.BehaviorSubject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jetbrains.annotations.NotNull;
import timber.log.Timber;
@Singleton
@ -37,23 +38,23 @@ public class UploadModel {
private final Context context;
private String license;
private final Map<String, String> licensesByName;
private List<UploadItem> items = new ArrayList<>();
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private final List<UploadItem> items = new ArrayList<>();
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
private SessionManager sessionManager;
private FileProcessor fileProcessor;
private final SessionManager sessionManager;
private final FileProcessor fileProcessor;
private final ImageProcessingService imageProcessingService;
private List<String> selectedCategories;
private ArrayList<String> selectedDepictions;
@Inject
UploadModel(@Named("licenses") List<String> licenses,
@Named("default_preferences") JsonKvStore store,
@Named("licenses_by_name") Map<String, String> licensesByName,
Context context,
SessionManager sessionManager,
FileProcessor fileProcessor,
ImageProcessingService imageProcessingService) {
UploadModel(@Named("licenses") final List<String> licenses,
@Named("default_preferences") final JsonKvStore store,
@Named("licenses_by_name") final Map<String, String> licensesByName,
final Context context,
final SessionManager sessionManager,
final FileProcessor fileProcessor,
final ImageProcessingService imageProcessingService) {
this.licenses = licenses;
this.store = store;
this.license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
@ -80,31 +81,29 @@ public class UploadModel {
}
public void setSelectedCategories(List<String> selectedCategories) {
if (null == selectedCategories) {
selectedCategories = new ArrayList<>();
}
this.selectedCategories = selectedCategories;
this.selectedCategories = newListOf(selectedCategories);
}
/**
* pre process a one item at a time
*/
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile,
Place place,
String source,
SimilarImageInterface similarImageInterface) {
return Observable.just(getUploadItem(uploadableFile, place, source, similarImageInterface));
public Observable<UploadItem> preProcessImage(final UploadableFile uploadableFile,
final Place place,
final String source,
final SimilarImageInterface similarImageInterface) {
return Observable.just(
createAndAddUploadItem(uploadableFile, place, source, similarImageInterface));
}
public Single<Integer> getImageQuality(UploadItem uploadItem) {
public Single<Integer> getImageQuality(final UploadItem uploadItem) {
return imageProcessingService.validateImage(uploadItem);
}
private UploadItem getUploadItem(UploadableFile uploadableFile,
Place place,
String source,
SimilarImageInterface similarImageInterface) {
UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile
private UploadItem createAndAddUploadItem(final UploadableFile uploadableFile,
final Place place,
final String source,
final SimilarImageInterface similarImageInterface) {
final UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile
.getFileCreatedDate(context);
long fileCreatedDate = -1;
String createdTimestampSource = "";
@ -113,23 +112,15 @@ public class UploadModel {
createdTimestampSource = dateTimeWithSource.getSource();
}
Timber.d("File created date is %d", fileCreatedDate);
ImageCoordinates imageCoordinates = fileProcessor
final ImageCoordinates imageCoordinates = fileProcessor
.processFileCoordinates(similarImageInterface, uploadableFile.getFilePath());
UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(),
final UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(),
Uri.parse(uploadableFile.getFilePath()),
uploadableFile.getMimeType(context), source, imageCoordinates, place, fileCreatedDate,
createdTimestampSource);
if (place != null) {
uploadItem.title.setTitleText(place.name);
if(uploadItem.uploadMediaDetails.isEmpty()) {
uploadItem.uploadMediaDetails.add(new UploadMediaDetail());
}
uploadItem.uploadMediaDetails.get(0).setDescriptionText(place.getLongDescription());
uploadItem.uploadMediaDetails.get(0).setLanguageCode("en");
String languageCode = Locale.getDefault().getLanguage();
uploadItem.uploadMediaDetails.get(0).setDescriptionText(place.getLongDescription());
uploadItem.uploadMediaDetails.get(0).setLanguageCode(languageCode);
uploadItem.uploadMediaDetails.get(0).setCaptionText(place.name);
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
}
if (!items.contains(uploadItem)) {
items.add(uploadItem);
@ -153,7 +144,7 @@ public class UploadModel {
return license;
}
public void setSelectedLicense(String licenseName) {
public void setSelectedLicense(final String licenseName) {
this.license = licensesByName.get(licenseName);
store.putString(Prefs.DEFAULT_LICENSE, license);
}
@ -161,11 +152,11 @@ public class UploadModel {
public Observable<Contribution> buildContributions() {
return Observable.fromIterable(items).map(item ->
{
Contribution contribution = new Contribution(item.mediaUri, null,
final Contribution contribution = new Contribution(item.mediaUri, null,
item.getFileName(), item.uploadMediaDetails.size()!=0? UploadMediaDetail.formatCaptions(item.uploadMediaDetails):new HashMap<>(),
UploadMediaDetail.formatList(item.uploadMediaDetails), -1,
null, null, sessionManager.getAuthorName(),
CommonsApplication.DEFAULT_EDIT_SUMMARY, selectedDepictions, item.gpsCoords.getDecimalCoords());
CommonsApplication.DEFAULT_EDIT_SUMMARY, new ArrayList<>(selectedDepictions), item.gpsCoords.getDecimalCoords());
if (item.place != null) {
contribution.setWikiDataEntityId(item.place.getWikiDataEntityId());
contribution.setWikiItemName(item.place.getName());
@ -196,8 +187,8 @@ public class UploadModel {
});
}
public void deletePicture(String filePath) {
Iterator<UploadItem> iterator = items.iterator();
public void deletePicture(final String filePath) {
final Iterator<UploadItem> iterator = items.iterator();
while (iterator.hasNext()) {
if (iterator.next().mediaUri.toString().contains(filePath)) {
iterator.remove();
@ -213,20 +204,22 @@ public class UploadModel {
return items;
}
public void updateUploadItem(int index, UploadItem uploadItem) {
UploadItem uploadItem1 = items.get(index);
public void updateUploadItem(final int index, final UploadItem uploadItem) {
final UploadItem uploadItem1 = items.get(index);
uploadItem1.setMediaDetails(uploadItem.uploadMediaDetails);
uploadItem1.setTitle(uploadItem.title);
}
public void setSelectedDepictions(List<String> selectedDepictions) {
if (null == selectedDepictions) {
selectedDepictions = new ArrayList<>();
}
this.selectedDepictions = (ArrayList<String>) selectedDepictions;
public void setSelectedDepictions(final List<String> selectedDepictions) {
this.selectedDepictions = newListOf(selectedDepictions);
}
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
@NotNull
private <T> ArrayList<T> newListOf(final List<T> items) {
return items != null ? new ArrayList<>(items) : new ArrayList<>();
}
public void useSimilarPictureCoordinates(final ImageCoordinates imageCoordinates, final int uploadItemIndex) {
fileProcessor.useImageCoords(imageCoordinates);
items.get(uploadItemIndex).setGpsCoords(imageCoordinates);
}
@ -239,29 +232,23 @@ public class UploadModel {
private final String mimeType;
private final String source;
private ImageCoordinates gpsCoords;
public void setGpsCoords(ImageCoordinates gpsCoords) {
this.gpsCoords = gpsCoords;
}
private Title title;
private List<UploadMediaDetail> uploadMediaDetails;
private Place place;
private long createdTimestamp;
private String createdTimestampSource;
private BehaviorSubject<Integer> imageQuality;
private final Place place;
private final long createdTimestamp;
private final String createdTimestampSource;
private final BehaviorSubject<Integer> imageQuality;
@SuppressLint("CheckResult")
UploadItem(Uri originalContentUri,
Uri mediaUri, String mimeType, String source, ImageCoordinates gpsCoords,
Place place,
long createdTimestamp,
String createdTimestampSource) {
UploadItem(final Uri originalContentUri,
final Uri mediaUri, final String mimeType, final String source, final ImageCoordinates gpsCoords,
final Place place,
final long createdTimestamp,
final String createdTimestampSource) {
this.originalContentUri = originalContentUri;
this.createdTimestampSource = createdTimestampSource;
title = new Title();
uploadMediaDetails = new ArrayList<>();
uploadMediaDetails.add(new UploadMediaDetail());
uploadMediaDetails = Collections.singletonList(new UploadMediaDetail());
uploadMediaDetails = new ArrayList<>(Arrays.asList(new UploadMediaDetail()));
this.place = place;
this.mediaUri = mediaUri;
this.mimeType = mimeType;
@ -303,7 +290,7 @@ public class UploadModel {
return this.imageQuality.getValue();
}
public void setImageQuality(int imageQuality) {
public void setImageQuality(final int imageQuality) {
this.imageQuality.onNext(imageQuality);
}
@ -311,12 +298,12 @@ public class UploadModel {
return place;
}
public void setTitle(Title title) {
public void setTitle(final Title title) {
this.title = title;
}
public void setMediaDetails(List<UploadMediaDetail> uploadMediaDetails) {
public void setMediaDetails(final List<UploadMediaDetail> uploadMediaDetails) {
this.uploadMediaDetails = uploadMediaDetails;
}
@ -325,7 +312,7 @@ public class UploadModel {
}
@Override
public boolean equals(@Nullable Object obj) {
public boolean equals(@Nullable final Object obj) {
if (!(obj instanceof UploadItem)) {
return false;
}
@ -345,6 +332,10 @@ public class UploadModel {
public String getFileName() {
return uploadMediaDetails.get(0).getCaptionText();
}
}
public void setGpsCoords(final ImageCoordinates gpsCoords) {
this.gpsCoords = gpsCoords;
}
}
}

View file

@ -13,6 +13,7 @@ import androidx.core.app.NotificationManagerCompat;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
@ -35,18 +36,8 @@ import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.wikidata.WikidataEditService;
import io.reactivex.Observable;
import io.reactivex.Scheduler;
import io.reactivex.SingleObserver;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import timber.log.Timber;
public class UploadService extends HandlerService<Contribution> {
@ -275,55 +266,59 @@ public class UploadService extends HandlerService<Contribution> {
uploadStash.getFilekey());
}
})
.subscribe(uploadResult -> {
Timber.d("Stash upload response 2 is %s", uploadResult.toString());
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
String resultStatus = uploadResult.getResult();
if (!resultStatus.equals("Success")) {
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
showFailedNotification(contribution);
} else {
String canonicalFilename = "File:" + uploadResult.getFilename();
final String wikiDataEntityId = contribution.getWikiDataEntityId();
Timber.d("Contribution upload success. Initiating Wikidata edit for entity id %s",
wikiDataEntityId);
// to perform upload of depictions we pass on depiction entityId of the selected depictions to the wikidataEditService
final String p18Value = contribution.getP18Value();
final String wikiItemName = contribution.getWikiItemName();
if (contribution.getDepictionsEntityIds() != null) {
for (String depictionEntityId : contribution.getDepictionsEntityIds()) {
wikidataEditService.createClaimWithLogging(depictionEntityId,
wikiItemName, canonicalFilename, p18Value);
}
}
Timber.d("Contribution upload success. Initiating Wikidata edit for"
+ " entity id %s if necessary (if P18 is null). P18 value is %s",
wikiDataEntityId, p18Value);
wikidataEditService.createClaimWithLogging(
wikiDataEntityId, wikiItemName, canonicalFilename,p18Value);
wikidataEditService.createLabelforWikidataEntity(
wikiDataEntityId, canonicalFilename, contribution.getCaptions());
contribution.setFilename(canonicalFilename);
contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl());
contribution.setState(Contribution.STATE_COMPLETED);
contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatShort()
.parse(uploadResult.getImageinfo().getTimestamp()));
compositeDisposable.add(contributionDao
.save(contribution)
.subscribeOn(ioThreadScheduler)
.observeOn(mainThreadScheduler)
.subscribe());
}
}, throwable -> {
.subscribe(
uploadResult -> onUpload(contribution, notificationTag, uploadResult),
throwable -> {
Timber.w(throwable, "Exception during upload");
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
showFailedNotification(contribution);
});
}
private void onUpload(Contribution contribution, String notificationTag,
UploadResult uploadResult) throws ParseException {
Timber.d("Stash upload response 2 is %s", uploadResult.toString());
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
String resultStatus = uploadResult.getResult();
if (!resultStatus.equals("Success")) {
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
showFailedNotification(contribution);
} else {
String canonicalFilename = "File:" + uploadResult.getFilename();
final String wikiDataEntityId = contribution.getWikiDataEntityId();
Timber.d("Contribution upload success. Initiating Wikidata edit for entity id %s",
wikiDataEntityId);
// to perform upload of depictions we pass on depiction entityId of the selected depictions to the wikidataEditService
final String p18Value = contribution.getP18Value();
final String wikiItemName = contribution.getWikiItemName();
if (contribution.getDepictionsEntityIds() != null) {
for (String depictionEntityId : contribution.getDepictionsEntityIds()) {
wikidataEditService.createClaimWithLogging(depictionEntityId,
wikiItemName, canonicalFilename, p18Value);
}
}
Timber.d("Contribution upload success. Initiating Wikidata edit for"
+ " entity id %s if necessary (if P18 is null). P18 value is %s",
wikiDataEntityId, p18Value);
wikidataEditService.createClaimWithLogging(
wikiDataEntityId, wikiItemName, canonicalFilename,p18Value);
wikidataEditService.createLabelforWikidataEntity(canonicalFilename, contribution.getCaptions());
contribution.setFilename(canonicalFilename);
contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl());
contribution.setState(Contribution.STATE_COMPLETED);
contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatShort()
.parse(uploadResult.getImageinfo().getTimestamp()));
compositeDisposable.add(contributionDao
.save(contribution)
.subscribeOn(ioThreadScheduler)
.observeOn(mainThreadScheduler)
.subscribe());
}
}
@SuppressLint("StringFormatInvalid")
@SuppressWarnings("deprecation")
private void showFailedNotification(Contribution contribution) {

View file

@ -32,14 +32,12 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.settings.Prefs;
import fr.free.nrw.commons.upload.Description;
//import fr.free.nrw.commons.upload.DescriptionsAdapter;
import fr.free.nrw.commons.upload.UploadMediaDetail;
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter;
import fr.free.nrw.commons.upload.ImageCoordinates;
import fr.free.nrw.commons.upload.SimilarImageDialogFragment;
import fr.free.nrw.commons.upload.Title;
import fr.free.nrw.commons.upload.UploadBaseFragment;
import fr.free.nrw.commons.upload.UploadMediaDetail;
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter;
import fr.free.nrw.commons.upload.UploadModel;
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
import fr.free.nrw.commons.utils.DialogUtil;
@ -55,6 +53,8 @@ import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import timber.log.Timber;
//import fr.free.nrw.commons.upload.DescriptionsAdapter;
public class UploadMediaDetailFragment extends UploadBaseFragment implements
UploadMediaDetailsContract.View, UploadMediaDetailAdapter.EventListener {
@ -282,10 +282,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
@Override
public void onImageProcessed(UploadItem uploadItem, Place place) {
this.uploadItem = uploadItem;
if (uploadItem.getFileName() != null) {
setDescriptionsInAdapter(uploadItem.getUploadMediaDetails());
}
descriptions = uploadItem.getUploadMediaDetails();
photoViewBackgroundImage.setImageURI(uploadItem.getMediaUri());
setDescriptionsInAdapter(descriptions);
@ -306,10 +302,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
place.getName()),
() -> {
etTitle.setText(place.getName());
UploadMediaDetail description = new UploadMediaDetail();
description.setLanguageCode("en");
description.setDescriptionText(place.getLongDescription());
descriptions = Arrays.asList(description);
descriptions = new ArrayList<>(Arrays.asList(new UploadMediaDetail()));
setDescriptionsInAdapter(descriptions);
},
() -> {
@ -431,13 +424,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
}
private void setDescriptionsInAdapter(List<UploadMediaDetail> uploadMediaDetails){
if(uploadMediaDetails==null){
uploadMediaDetails=new ArrayList<>();
}
if(uploadMediaDetails.size()==0){
uploadMediaDetails.add(new UploadMediaDetail());
}
uploadMediaDetailAdapter.setItems(uploadMediaDetails);
}
}

View file

@ -10,7 +10,6 @@ import com.google.gson.JsonObject;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.upload.mediaDetails.CaptionInterface;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.ViewUtil;
@ -26,7 +25,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.wikipedia.csrf.CsrfTokenClient;
import org.wikipedia.dataclient.Service;
import timber.log.Timber;
/**
@ -46,42 +44,31 @@ public class WikidataEditService {
private final CaptionInterface captionInterface;
private final WikiBaseClient wikiBaseClient;
private final WikidataClient wikidataClient;
private final MediaClient mediaClient;
private final CsrfTokenClient csrfTokenClient;
private final Service service;
@Inject
public WikidataEditService(Context context,
WikidataEditListener wikidataEditListener,
MediaClient mediaClient,
@Named("default_preferences") JsonKvStore directKvStore,
WikiBaseClient wikiBaseClient,
CaptionInterface captionInterface,
WikidataClient wikidataClient,
@Named("commons-csrf") CsrfTokenClient csrfTokenClient,
@Named("commons-service") Service service) {
public WikidataEditService(final Context context,
final WikidataEditListener wikidataEditListener,
@Named("default_preferences") final JsonKvStore directKvStore,
final WikiBaseClient wikiBaseClient,
final CaptionInterface captionInterface,
final WikidataClient wikidataClient,
@Named("commons-csrf") final CsrfTokenClient csrfTokenClient) {
this.context = context;
this.wikidataEditListener = wikidataEditListener;
this.directKvStore = directKvStore;
this.captionInterface = captionInterface;
this.wikiBaseClient = wikiBaseClient;
this.mediaClient = mediaClient;
this.wikidataClient = wikidataClient;
this.csrfTokenClient = csrfTokenClient;
this.service = service;
}
}
/**
* Create a P18 claim and log the edit with custom tag
<<<<<<< HEAD
*
* @param wikidataEntityId
* @param fileName
=======
* @param wikidataEntityId a unique id of each Wikidata items
* @param fileName name of the file we will upload
* @param p18Value pic attribute of Wikidata item
>>>>>>> origin/master
*/
public void createClaimWithLogging(String wikidataEntityId, String wikiItemName, String fileName, String p18Value) {
if (wikidataEntityId == null) {
@ -104,8 +91,8 @@ public class WikidataEditService {
return;
}
editWikidataProperty(wikidataEntityId, wikiItemName, fileName);
//editWikiBaseDepictsProperty(wikidataEntityId, fileName);
editWikidataProperty(wikidataEntityId, wikiItemName, fileName);;
editWikiBaseDepictsProperty(wikidataEntityId, fileName);
}
@ -122,7 +109,7 @@ public class WikidataEditService {
Timber.d("Upload successful with wiki data entity id as %s", wikidataEntityId);
Timber.d("Attempting to edit Wikidata property %s", wikidataEntityId);
String propertyValue = getFileName(fileName);
final String propertyValue = getFileName(fileName);
Timber.d("Entity id is %s and property value is %s", wikidataEntityId, propertyValue);
wikidataClient.createClaim(wikidataEntityId, propertyValue)
@ -148,7 +135,7 @@ public class WikidataEditService {
* @param fileName
*/
@SuppressLint("CheckResult")
private void editWikiBaseDepictsProperty(String wikidataEntityId, String fileName) {
private void editWikiBaseDepictsProperty(final String wikidataEntityId, final String fileName) {
wikiBaseClient.getFileEntityId(fileName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -166,37 +153,37 @@ public class WikidataEditService {
}
@SuppressLint("CheckResult")
private void addDepictsProperty(String entityId, String fileEntityId) {
private void addDepictsProperty(String entityId, final String fileEntityId) {
if (ConfigUtils.isBetaFlavour()) {
entityId = "Q10"; // Wikipedia:Sandbox (Q10)
}
JsonObject value = new JsonObject();
final JsonObject value = new JsonObject();
value.addProperty("entity-type", "item");
value.addProperty("numeric-id", entityId.replace("Q", ""));
value.addProperty("id", entityId);
JsonObject dataValue = new JsonObject();
final JsonObject dataValue = new JsonObject();
dataValue.add("value", value);
dataValue.addProperty("type", "wikibase-entityid");
JsonObject mainSnak = new JsonObject();
final JsonObject mainSnak = new JsonObject();
mainSnak.addProperty("snaktype", "value");
mainSnak.addProperty("property", BuildConfig.DEPICTS_PROPERTY);
mainSnak.add("datavalue", dataValue);
JsonObject claim = new JsonObject();
final JsonObject claim = new JsonObject();
claim.add("mainsnak", mainSnak);
claim.addProperty("type", "statement");
claim.addProperty("rank", "preferred");
JsonArray claims = new JsonArray();
final JsonArray claims = new JsonArray();
claims.add(claim);
JsonObject jsonData = new JsonObject();
final JsonObject jsonData = new JsonObject();
jsonData.add("claims", claims);
String data = jsonData.toString();
final String data = jsonData.toString();
Observable.defer((Callable<ObservableSource<Boolean>>) () ->
wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, data))
@ -253,18 +240,19 @@ public class WikidataEditService {
* Adding captions as labels after image is successfully uploaded
*/
@SuppressLint("CheckResult")
public void createLabelforWikidataEntity(String wikiDataEntityId, String fileName, Map<String, String> captions) {
public void createLabelforWikidataEntity(final String fileName,
final Map<String, String> captions) {
Observable.fromCallable(() -> wikiBaseClient.getFileEntityId(fileName))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(fileEntityId -> {
if (fileEntityId != null) {
for (Map.Entry<String, String> entry : captions.entrySet()) {
Map<String, String> caption = new HashMap<>();
for (final Map.Entry<String, String> entry : captions.entrySet()) {
final Map<String, String> caption = new HashMap<>();
caption.put(entry.getKey(), entry.getValue());
try {
wikidataAddLabels(wikiDataEntityId, fileEntityId.toString(), caption);
} catch (Throwable throwable) {
wikidataAddLabels(fileEntityId.toString(), caption);
} catch (final Throwable throwable) {
throwable.printStackTrace();
}
}
@ -280,13 +268,12 @@ public class WikidataEditService {
/**
* Adds label to Wikidata using the fileEntityId and the edit token, obtained from csrfTokenClient
*
* @param wikiDataEntityId entityId for the current contribution
* @param fileEntityId
* @param caption
*/
@SuppressLint("CheckResult")
private void wikidataAddLabels(String wikiDataEntityId, String fileEntityId, Map<String, String> caption) throws Throwable {
private void wikidataAddLabels(final String fileEntityId, final Map<String, String> caption) {
Observable.fromCallable(() -> {
try {
return csrfTokenClient.getTokenBlocking();

View file

@ -2,12 +2,14 @@ package fr.free.nrw.commons.wikidata
import android.content.Context
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse
import io.reactivex.Observable
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyString
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
@ -15,19 +17,19 @@ import org.mockito.MockitoAnnotations
class WikidataEditServiceTest {
@Mock
internal var context: Context? = null
@Mock
internal var wikidataEditListener: WikidataEditListener? = null
@Mock
internal var directKvStore: JsonKvStore? = null
@Mock
internal var wikidataClient: WikidataClient? = null
internal lateinit var context: Context
@Mock
internal var wikibaseClient: WikiBaseClient? = null
internal lateinit var directKvStore: JsonKvStore
@Mock
internal lateinit var wikidataClient: WikidataClient
@Mock
internal lateinit var wikibaseClient: WikiBaseClient
@InjectMocks
var wikidataEditService: WikidataEditService? = null
lateinit var wikidataEditService: WikidataEditService
@Before
@Throws(Exception::class)
@ -37,40 +39,40 @@ class WikidataEditServiceTest {
@Test
fun noClaimsWhenEntityIdIsNull() {
wikidataEditService!!.createClaimWithLogging(null, null,"Test.jpg","")
verifyZeroInteractions(wikidataClient!!)
wikidataEditService.createClaimWithLogging(null, null,"Test.jpg","")
verifyZeroInteractions(wikidataClient)
}
@Test
fun noClaimsWhenFileNameIsNull() {
wikidataEditService!!.createClaimWithLogging("Q1", "Test", null,"")
verifyZeroInteractions(wikidataClient!!)
wikidataEditService.createClaimWithLogging("Q1", "Test", null,"")
verifyZeroInteractions(wikidataClient)
}
@Test
fun noClaimsWhenP18IsNotEmpty() {
wikidataEditService!!.createClaimWithLogging("Q1", "Test","Test.jpg","Previous.jpg")
verifyZeroInteractions(wikidataClient!!)
wikidataEditService.createClaimWithLogging("Q1", "Test","Test.jpg","Previous.jpg")
verifyZeroInteractions(wikidataClient)
}
@Test
fun noClaimsWhenLocationIsNotCorrect() {
`when`(directKvStore!!.getBoolean("Picture_Has_Correct_Location", true))
.thenReturn(false)
wikidataEditService!!.createClaimWithLogging("Q1", "","Test.jpg","")
verifyZeroInteractions(wikidataClient!!)
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
.thenReturn(false)
wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "")
verifyZeroInteractions(wikidataClient)
}
@Test
fun createClaimWithLogging() {
`when`(directKvStore!!.getBoolean("Picture_Has_Correct_Location", true))
.thenReturn(true)
`when`(wikidataClient!!.createClaim(ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
.thenReturn(Observable.just(1L))
`when`(wikidataClient!!.addEditTag(anyLong(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mock(AddEditTagResponse::class.java)))
wikidataEditService!!.createClaimWithLogging("Q1", "Test","Test.jpg","")
verify(wikidataClient!!, times(1))
.createClaim(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
.thenReturn(true)
whenever(wikidataClient.createClaim(anyString(), anyString()))
.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))
wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "")
verify(wikidataClient, times(1)).createClaim(anyString(), anyString())
}
}
}