From 0914eeea530468634838b0ebe09aed2f0e898e0e Mon Sep 17 00:00:00 2001 From: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Date: Wed, 19 Jan 2022 20:40:55 +0530 Subject: [PATCH] Fixed #4532 Items media in beta version are showing now (#4551) * logs * Issue resolved * Logs removed * Beta working * Production GET call only for fetch images from depicted item * Code convention maintained * Code convention maintained * Test resolved * Java Docs added * Quotes added --- .../java/fr/free/nrw/commons/BetaConstants.kt | 18 ++++ .../free/nrw/commons/di/NetworkingModule.java | 16 ++++ .../media/PageableDepictedMediaDataSource.kt | 8 +- .../nrw/commons/media/WikidataMediaClient.kt | 93 +++++++++++++++++++ .../commons/media/WikidataMediaInterface.java | 29 ++++++ .../PageableDepictedMediaDataSourceTest.kt | 3 +- 6 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/BetaConstants.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.java diff --git a/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt b/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt new file mode 100644 index 000000000..018d787f9 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt @@ -0,0 +1,18 @@ +package fr.free.nrw.commons + +/** + * Production variant related constants which is used in beta variant for some specific GET calls on + * production server where beta server does not work + */ +object BetaConstants { + /** + * Commons production URL which is used in beta for some specific GET calls on + * production server where beta server does not work + */ + const val COMMONS_URL = "https://commons.wikimedia.org/" + /** + * Commons production's depicts property which is used in beta for some specific GET calls on + * production server where beta server does not work + */ + const val DEPICTS_PROPERTY = "P180" +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java index ec68c3eae..5582a8a91 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java @@ -5,6 +5,7 @@ import androidx.annotation.NonNull; import com.google.gson.Gson; import dagger.Module; import dagger.Provides; +import fr.free.nrw.commons.BetaConstants; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.actions.PageEditClient; import fr.free.nrw.commons.actions.PageEditInterface; @@ -14,6 +15,7 @@ import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.media.MediaDetailInterface; import fr.free.nrw.commons.media.MediaInterface; import fr.free.nrw.commons.media.PageMediaInterface; +import fr.free.nrw.commons.media.WikidataMediaInterface; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.UserInterface; import fr.free.nrw.commons.review.ReviewInterface; @@ -226,6 +228,20 @@ public class NetworkingModule { return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, MediaInterface.class); } + /** + * Add provider for WikidataMediaInterface + * It creates a retrofit service for the commons wiki site + * @param commonsWikiSite commonsWikiSite + * @return WikidataMediaInterface + */ + @Provides + @Singleton + public WikidataMediaInterface provideWikidataMediaInterface( + @Named(NAMED_COMMONS_WIKI_SITE) final WikiSite commonsWikiSite) { + return ServiceFactory.get(commonsWikiSite, + BetaConstants.COMMONS_URL, WikidataMediaInterface.class); + } + @Provides @Singleton public MediaDetailInterface providesMediaDetailInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikisite) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt index 912d47aba..9592d7605 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt @@ -1,17 +1,17 @@ package fr.free.nrw.commons.explore.depictions.media import fr.free.nrw.commons.Media +import fr.free.nrw.commons.explore.depictions.search.LoadFunction import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource -import fr.free.nrw.commons.explore.depictions.search.LoadFunction -import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.media.WikidataMediaClient import javax.inject.Inject class PageableDepictedMediaDataSource @Inject constructor( liveDataConverter: LiveDataConverter, - private val mediaClient: MediaClient + private val wikiMediaClient: WikidataMediaClient ) : PageableBaseDataSource(liveDataConverter) { override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> - mediaClient.fetchImagesForDepictedItem(query, loadSize, startPosition).blockingGet() + wikiMediaClient.fetchImagesForDepictedItem(query, loadSize, startPosition).blockingGet() } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt new file mode 100644 index 000000000..d14123d21 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt @@ -0,0 +1,93 @@ +package fr.free.nrw.commons.media + +import fr.free.nrw.commons.BetaConstants +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.category.ContinuationClient +import fr.free.nrw.commons.explore.media.MediaConverter +import io.reactivex.Single +import org.wikipedia.dataclient.mwapi.MwQueryPage +import org.wikipedia.dataclient.mwapi.MwQueryResponse +import org.wikipedia.wikidata.Entities +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Media Client to handle custom calls to Commons MediaWiki APIs of production server + */ +@Singleton +class WikidataMediaClient @Inject constructor( + private val wikidataMediaInterface: WikidataMediaInterface, + private val mediaDetailInterface: MediaDetailInterface, + private val mediaConverter: MediaConverter +) : ContinuationClient() { + + /** + * Fetch images for depict ID + * + * @param query depictionEntityId ex. "Q9394" + * @param srlimit the number of items to fetch + * @param sroffset number of depictions already fetched, + * this is useful in implementing pagination + * @return list of images for a particular depict ID + */ + fun fetchImagesForDepictedItem( + query: String, + srlimit: Int, + sroffset: Int + ): Single> { + return responseMapper( + wikidataMediaInterface.fetchImagesForDepictedItem( + "haswbstatement:" + BetaConstants.DEPICTS_PROPERTY + "=" + query, + srlimit.toString(), + sroffset.toString() + ) + ) + } + + /** + * Helps to map to the required data from the API response + * + * @param networkResult MwQueryResponse + * @param key for handling continuation request, this is null in this case + */ + override fun responseMapper( + networkResult: Single, + key: String? + ): Single> { + return networkResult.map { + handleContinuationResponse(it.continuation(), key) + it.query()?.pages() ?: emptyList() + }.flatMap(::mediaFromPageAndEntity) + } + + /** + * Gets list of Media from MwQueryPage + */ + private fun mediaFromPageAndEntity(pages: List): Single> { + return if (pages.isEmpty()) + Single.just(emptyList()) + else + getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" }) + .map { + pages.zip(it.entities().values) + .mapNotNull { (page, entity) -> + page.imageInfo()?.let { + mediaConverter.convert(page, entity, it) + } + } + } + } + + /** + * Gets Entities from IDs + * + * @param entityIds list of IDs of pages/entities ex. {"M4254154", "M11413343"} + */ + fun getEntities(entityIds: List): Single { + return if (entityIds.isEmpty()) + Single.error(Exception("empty list passed for ids")) + else + mediaDetailInterface.getEntity(entityIds.joinToString("|")) + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.java b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.java new file mode 100644 index 000000000..62e270ab9 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.java @@ -0,0 +1,29 @@ +package fr.free.nrw.commons.media; + +import static fr.free.nrw.commons.media.MediaInterface.MEDIA_PARAMS; + +import io.reactivex.Single; +import org.wikipedia.dataclient.mwapi.MwQueryResponse; +import retrofit2.http.GET; +import retrofit2.http.Query; + +/** + * Interface for getting Wikidata images from production server + */ +public interface WikidataMediaInterface { + + /** + * Fetches list of images from a depiction entity + * @param query depictionEntityId ex. "haswbstatement:P180=Q9394" + * @param srlimit the number of items to fetch + * @param sroffset number of depictions already fetched, + * this is useful in implementing pagination + * @return Single + */ + @GET("w/api.php?action=query&format=json&formatversion=2" + //Basic parameters + "&generator=search&gsrnamespace=6" + //Search parameters + MEDIA_PARAMS) + Single fetchImagesForDepictedItem(@Query("gsrsearch") String query, + @Query("gsrlimit")String srlimit, @Query("gsroffset") String sroffset); + +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt index 22049d899..c65ccb702 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt @@ -3,6 +3,7 @@ package fr.free.nrw.commons.explore.depictions.media import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.media.WikidataMediaClient import io.reactivex.Single import org.junit.Assert import org.junit.Test @@ -10,7 +11,7 @@ import org.junit.Test class PageableDepictedMediaDataSourceTest{ @Test fun `loadFunction loads Media`() { - val mediaClient = mock() + val mediaClient = mock() whenever(mediaClient.fetchImagesForDepictedItem("test",0,1)) .thenReturn(Single.just(emptyList())) val pageableDepictedMediaDataSource = PageableDepictedMediaDataSource(mock(), mediaClient)