Added image duplicity check on restart of failed image

This commit is contained in:
Kanahia 2024-07-31 17:45:43 +05:30
parent ae00a9d19f
commit e96cc8b5ca
6 changed files with 135 additions and 47 deletions

View file

@ -5,6 +5,7 @@ import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED; import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED;
import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL; import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL;
import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME; import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
import static fr.free.nrw.commons.utils.LengthUtils.computeBearing; import static fr.free.nrw.commons.utils.LengthUtils.computeBearing;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
@ -831,10 +832,18 @@ public class ContributionsFragment
* @param contribution * @param contribution
*/ */
public void restartUpload(Contribution contribution) { public void restartUpload(Contribution contribution) {
contribution.setState(Contribution.STATE_QUEUED);
contribution.setDateUploadStarted(Calendar.getInstance().getTime()); contribution.setDateUploadStarted(Calendar.getInstance().getTime());
contributionsPresenter.saveContribution(contribution); if (contribution.getState() == Contribution.STATE_FAILED) {
Timber.d("Restarting for %s", contribution.toString()); if (contribution.getErrorInfo() == null){
contribution.setChunkInfo(null);
contribution.setTransferred(0);
}
contributionsPresenter.checkDuplicateImageAndRestartContribution(contribution);
} else {
contribution.setState(Contribution.STATE_QUEUED);
contributionsPresenter.saveContribution(contribution);
Timber.d("Restarting for %s", contribution.toString());
}
} }
/** /**
@ -856,10 +865,6 @@ public class ContributionsFragment
contribution.setRetries(retries + 1); contribution.setRetries(retries + 1);
Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(), Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(),
retries + 1); retries + 1);
if (contribution.getErrorInfo() == null){
contribution.setChunkInfo(null);
contribution.setTransferred(0);
}
restartUpload(contribution); restartUpload(contribution);
} else { } else {
// TODO: Show the exact reason for failure // TODO: Show the exact reason for failure

View file

@ -1,21 +1,26 @@
package fr.free.nrw.commons.contributions; package fr.free.nrw.commons.contributions;
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
import androidx.work.ExistingWorkPolicy; import androidx.work.ExistingWorkPolicy;
import fr.free.nrw.commons.MediaDataExtractor; import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener; import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
import fr.free.nrw.commons.di.CommonsApplicationModule; import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.repository.UploadRepository;
import fr.free.nrw.commons.upload.worker.WorkRequestHelper; import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
import io.reactivex.Scheduler; import io.reactivex.Scheduler;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import timber.log.Timber;
/** /**
* The presenter class for Contributions * The presenter class for Contributions
*/ */
public class ContributionsPresenter implements UserActionListener { public class ContributionsPresenter implements UserActionListener {
private final ContributionsRepository repository; private final ContributionsRepository contributionsRepository;
private final UploadRepository uploadRepository;
private final Scheduler ioThreadScheduler; private final Scheduler ioThreadScheduler;
private CompositeDisposable compositeDisposable; private CompositeDisposable compositeDisposable;
private ContributionsContract.View view; private ContributionsContract.View view;
@ -25,8 +30,10 @@ public class ContributionsPresenter implements UserActionListener {
@Inject @Inject
ContributionsPresenter(ContributionsRepository repository, ContributionsPresenter(ContributionsRepository repository,
UploadRepository uploadRepository,
@Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) { @Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) {
this.repository = repository; this.contributionsRepository = repository;
this.uploadRepository = uploadRepository;
this.ioThreadScheduler=ioThreadScheduler; this.ioThreadScheduler=ioThreadScheduler;
} }
@ -44,7 +51,25 @@ public class ContributionsPresenter implements UserActionListener {
@Override @Override
public Contribution getContributionsWithTitle(String title) { public Contribution getContributionsWithTitle(String title) {
return repository.getContributionWithFileName(title); return contributionsRepository.getContributionWithFileName(title);
}
public void checkDuplicateImageAndRestartContribution(Contribution contribution){
compositeDisposable.add(uploadRepository
.checkDuplicateImage(contribution.getLocalUriPath().getPath())
.subscribeOn(ioThreadScheduler)
.subscribe(imageCheckResult -> {
if (imageCheckResult == IMAGE_OK) {
contribution.setState(Contribution.STATE_QUEUED);
saveContribution(contribution);
}else {
Timber.e("Contribution already exists");
compositeDisposable.add(contributionsRepository
.deleteContributionFromDB(contribution)
.subscribeOn(ioThreadScheduler)
.subscribe());
}
}));
} }
/** /**
@ -54,7 +79,7 @@ public class ContributionsPresenter implements UserActionListener {
* @param contribution * @param contribution
*/ */
public void saveContribution(Contribution contribution) { public void saveContribution(Contribution contribution) {
compositeDisposable.add(repository compositeDisposable.add(contributionsRepository
.save(contribution) .save(contribution)
.subscribeOn(ioThreadScheduler) .subscribeOn(ioThreadScheduler)
.subscribe(() -> WorkRequestHelper.Companion.makeOneTimeWorkRequest( .subscribe(() -> WorkRequestHelper.Companion.makeOneTimeWorkRequest(

View file

@ -203,6 +203,16 @@ public class UploadRepository {
return uploadModel.getImageQuality(uploadItem, location); return uploadModel.getImageQuality(uploadItem, location);
} }
/**
* query the RemoteDataSource for image duplicity check
*
* @param filePath file to be checked
* @return IMAGE_DUPLICATE or IMAGE_OK
*/
public Single<Integer> checkDuplicateImage(String filePath) {
return uploadModel.checkDuplicateImage(filePath);
}
/** /**
* query the RemoteDataSource for caption quality * query the RemoteDataSource for caption quality
* *

View file

@ -140,7 +140,7 @@ public class ImageProcessingService {
* @param filePath file to be checked * @param filePath file to be checked
* @return IMAGE_DUPLICATE or IMAGE_OK * @return IMAGE_DUPLICATE or IMAGE_OK
*/ */
private Single<Integer> checkDuplicateImage(String filePath) { Single<Integer> checkDuplicateImage(String filePath) {
Timber.d("Checking for duplicate image %s", filePath); Timber.d("Checking for duplicate image %s", filePath);
return Single.fromCallable(() -> fileUtilsWrapper.getFileInputStream(filePath)) return Single.fromCallable(() -> fileUtilsWrapper.getFileInputStream(filePath))
.map(fileUtilsWrapper::getSHA1) .map(fileUtilsWrapper::getSHA1)

View file

@ -1,6 +1,8 @@
package fr.free.nrw.commons.upload; package fr.free.nrw.commons.upload;
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
@ -14,6 +16,7 @@ import fr.free.nrw.commons.contributions.ContributionBoundaryCallback;
import fr.free.nrw.commons.contributions.ContributionsRemoteDataSource; import fr.free.nrw.commons.contributions.ContributionsRemoteDataSource;
import fr.free.nrw.commons.contributions.ContributionsRepository; import fr.free.nrw.commons.contributions.ContributionsRepository;
import fr.free.nrw.commons.di.CommonsApplicationModule; import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.repository.UploadRepository;
import fr.free.nrw.commons.upload.PendingUploadsContract.UserActionListener; import fr.free.nrw.commons.upload.PendingUploadsContract.UserActionListener;
import fr.free.nrw.commons.upload.PendingUploadsContract.View; import fr.free.nrw.commons.upload.PendingUploadsContract.View;
import fr.free.nrw.commons.upload.worker.WorkRequestHelper; import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
@ -33,7 +36,8 @@ import timber.log.Timber;
public class PendingUploadsPresenter implements UserActionListener { public class PendingUploadsPresenter implements UserActionListener {
private final ContributionBoundaryCallback contributionBoundaryCallback; private final ContributionBoundaryCallback contributionBoundaryCallback;
private final ContributionsRepository repository; private final ContributionsRepository contributionsRepository;
private final UploadRepository uploadRepository;
private final Scheduler ioThreadScheduler; private final Scheduler ioThreadScheduler;
private final CompositeDisposable compositeDisposable; private final CompositeDisposable compositeDisposable;
@ -46,10 +50,12 @@ public class PendingUploadsPresenter implements UserActionListener {
PendingUploadsPresenter( PendingUploadsPresenter(
final ContributionBoundaryCallback contributionBoundaryCallback, final ContributionBoundaryCallback contributionBoundaryCallback,
final ContributionsRemoteDataSource contributionsRemoteDataSource, final ContributionsRemoteDataSource contributionsRemoteDataSource,
final ContributionsRepository repository, final ContributionsRepository contributionsRepository,
final UploadRepository uploadRepository,
@Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) { @Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) {
this.contributionBoundaryCallback = contributionBoundaryCallback; this.contributionBoundaryCallback = contributionBoundaryCallback;
this.repository = repository; this.contributionsRepository = contributionsRepository;
this.uploadRepository = uploadRepository;
this.ioThreadScheduler = ioThreadScheduler; this.ioThreadScheduler = ioThreadScheduler;
this.contributionsRemoteDataSource = contributionsRemoteDataSource; this.contributionsRemoteDataSource = contributionsRemoteDataSource;
compositeDisposable = new CompositeDisposable(); compositeDisposable = new CompositeDisposable();
@ -68,7 +74,7 @@ public class PendingUploadsPresenter implements UserActionListener {
.setPageSize(10).build(); .setPageSize(10).build();
Factory<Integer, Contribution> factory; Factory<Integer, Contribution> factory;
factory = repository.fetchContributionsWithStatesSortedByDateUploadStarted( factory = contributionsRepository.fetchContributionsWithStatesSortedByDateUploadStarted(
Arrays.asList(Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS, Arrays.asList(Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS,
Contribution.STATE_PAUSED)); Contribution.STATE_PAUSED));
LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory,
@ -82,7 +88,7 @@ public class PendingUploadsPresenter implements UserActionListener {
.setPrefetchDistance(50) .setPrefetchDistance(50)
.setPageSize(10).build(); .setPageSize(10).build();
Factory<Integer, Contribution> factory; Factory<Integer, Contribution> factory;
factory = repository.fetchContributionsWithStatesSortedByDateUploadStarted( factory = contributionsRepository.fetchContributionsWithStatesSortedByDateUploadStarted(
Collections.singletonList(Contribution.STATE_FAILED)); Collections.singletonList(Contribution.STATE_FAILED));
LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory,
pagedListConfig); pagedListConfig);
@ -103,7 +109,7 @@ public class PendingUploadsPresenter implements UserActionListener {
@Override @Override
public void deleteUpload(final Contribution contribution, Context context) { public void deleteUpload(final Contribution contribution, Context context) {
compositeDisposable.add(repository compositeDisposable.add(contributionsRepository
.deleteContributionFromDB(contribution) .deleteContributionFromDB(contribution)
.subscribeOn(ioThreadScheduler) .subscribeOn(ioThreadScheduler)
.subscribe()); .subscribe());
@ -111,14 +117,14 @@ public class PendingUploadsPresenter implements UserActionListener {
public void pauseUploads(List<Integer> states, int newState) { public void pauseUploads(List<Integer> states, int newState) {
CommonsApplication.isPaused = true ; CommonsApplication.isPaused = true ;
compositeDisposable.add(repository compositeDisposable.add(contributionsRepository
.updateContributionWithStates(states, newState) .updateContributionWithStates(states, newState)
.subscribeOn(ioThreadScheduler) .subscribeOn(ioThreadScheduler)
.subscribe()); .subscribe());
} }
public void deleteUploads(List<Integer> states) { public void deleteUploads(List<Integer> states) {
compositeDisposable.add(repository compositeDisposable.add(contributionsRepository
.deleteContributionsFromDBWithStates(states) .deleteContributionsFromDBWithStates(states)
.subscribeOn(ioThreadScheduler) .subscribeOn(ioThreadScheduler)
.subscribe()); .subscribe());
@ -132,27 +138,46 @@ public class PendingUploadsPresenter implements UserActionListener {
Contribution it = contributionList.get(index); Contribution it = contributionList.get(index);
if (it.getState() == Contribution.STATE_FAILED) { if (it.getState() == Contribution.STATE_FAILED) {
it.setDateUploadStarted(Calendar.getInstance().getTime()); it.setDateUploadStarted(Calendar.getInstance().getTime());
if (it.getErrorInfo() == null){ if (it.getErrorInfo() == null) {
it.setChunkInfo(null); it.setChunkInfo(null);
it.setTransferred(0); it.setTransferred(0);
} }
} compositeDisposable.add(uploadRepository
it.setState(Contribution.STATE_QUEUED); .checkDuplicateImage(it.getLocalUriPath().getPath())
compositeDisposable.add(repository .subscribeOn(ioThreadScheduler)
.save(it) .subscribe(imageCheckResult -> {
.subscribeOn(ioThreadScheduler) if (imageCheckResult == IMAGE_OK) {
.doOnComplete(() -> { it.setState(Contribution.STATE_QUEUED);
restartUploads(contributionList, index + 1, context); compositeDisposable.add(contributionsRepository
} .save(it)
) .subscribeOn(ioThreadScheduler)
.subscribe(() -> .doOnComplete(() -> {
WorkRequestHelper.Companion.makeOneTimeWorkRequest( restartUploads(contributionList, index + 1, context);
context, ExistingWorkPolicy.KEEP), })
throwable -> { .subscribe());
} else {
Timber.e("Contribution already exists");
compositeDisposable.add(contributionsRepository
.deleteContributionFromDB(it)
.subscribeOn(ioThreadScheduler).doOnComplete(() -> {
restartUploads(contributionList, index + 1, context);
})
.subscribe());
}
}, throwable -> {
Timber.e(throwable); Timber.e(throwable);
restartUploads(contributionList, index + 1, context); restartUploads(contributionList, index + 1, context);
} }));
)); } else {
it.setState(Contribution.STATE_QUEUED);
compositeDisposable.add(contributionsRepository
.save(it)
.subscribeOn(ioThreadScheduler)
.doOnComplete(() -> {
restartUploads(contributionList, index + 1, context);
})
.subscribe());
}
} }
public void restartUpload(List<Contribution> contributionList, int index, Context context) { public void restartUpload(List<Contribution> contributionList, int index, Context context) {
@ -167,18 +192,31 @@ public class PendingUploadsPresenter implements UserActionListener {
it.setChunkInfo(null); it.setChunkInfo(null);
it.setTransferred(0); it.setTransferred(0);
} }
compositeDisposable.add(uploadRepository
.checkDuplicateImage(it.getLocalUriPath().getPath())
.subscribeOn(ioThreadScheduler)
.subscribe(imageCheckResult -> {
if (imageCheckResult == IMAGE_OK) {
it.setState(Contribution.STATE_QUEUED);
compositeDisposable.add(contributionsRepository
.save(it)
.subscribeOn(ioThreadScheduler)
.subscribe());
}else {
Timber.e("Contribution already exists");
compositeDisposable.add(contributionsRepository
.deleteContributionFromDB(it)
.subscribeOn(ioThreadScheduler)
.subscribe());
}
}));
} else {
it.setState(Contribution.STATE_QUEUED);
compositeDisposable.add(contributionsRepository
.save(it)
.subscribeOn(ioThreadScheduler)
.subscribe());
} }
it.setState(Contribution.STATE_QUEUED);
compositeDisposable.add(repository
.save(it)
.subscribeOn(ioThreadScheduler)
.subscribe(() ->
WorkRequestHelper.Companion.makeOneTimeWorkRequest(
context, ExistingWorkPolicy.KEEP),
throwable -> {
Timber.e(throwable);
}
));
} }
} }

View file

@ -103,6 +103,16 @@ public class UploadModel {
return imageProcessingService.validateImage(uploadItem, inAppPictureLocation); return imageProcessingService.validateImage(uploadItem, inAppPictureLocation);
} }
/**
* Calls checkDuplicateImage() of ImageProcessingService to check if image is duplicate
*
* @param filePath file to be checked
* @return IMAGE_DUPLICATE or IMAGE_OK
*/
public Single<Integer> checkDuplicateImage(String filePath){
return imageProcessingService.checkDuplicateImage(filePath);
}
/** /**
* Calls validateCaption() of ImageProcessingService to check caption of image * Calls validateCaption() of ImageProcessingService to check caption of image
* *