Skip to content

Commit 4ffe59c

Browse files
macgillsashishkumar468
authored andcommitted
commons-app#3780 Create media using a combination of Entities & MwQueryResult (commons-app#3786)
* commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace SearchDepictionsRenderer * commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace UploadCategoryDepictionsRenderer * commons-app#3468 Switch from RvRenderer to AdapterDelegates - update BaseAdapter to be easier to use * commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace SearchImagesRenderer * commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace SearchCategoriesRenderer * commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace NotificationRenderer * commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace UploadDepictsRenderer * commons-app#3468 Switch from RvRenderer to AdapterDelegates - replace PlaceRenderer * commons-app#3756 Convert SearchDepictionsFragment to use Pagination - convert SearchDepictionsFragment * commons-app#3756 Convert SearchDepictionsFragment to use Pagination - fix presenter unit tests now that view is not nullable - fix Category prefix imports * commons-app#3756 Convert SearchDepictionsFragment to use Pagination - test DataSource related classes * commons-app#3756 Convert SearchDepictionsFragment to use Pagination - reset rx scheduler - ignore failing test * commons-app#3760 Convert SearchCategoriesFragment to use Pagination - extract functionality of pagination to base classes - add category pagination * commons-app#3772 Convert SearchImagesFragment to use Pagination - convert SearchImagesFragment - tidy up showing the empty view - make search fragments show snackbar with appropriate text * commons-app#3772 Convert SearchImagesFragment to use Pagination - allow viewpager to load more data * commons-app#3760 remove test that got re-added by merge * commons-app#3760 remove duplicate dependency * commons-app#3772 fix compilation * commons-app#3780 Create media using a combination of Entities & MwQueryResult - construct media with an entity - move fields from media down to contribution - move dynamic fields outside of media - remove unused constructors - remove all unnecessary fetching of captions/descriptions - bump database version * commons-app#3808 Construct media objects that depict an item id correctly - use generator to construct media for DepictedImages * commons-app#3780 Create media using a combination of Entities & MwQueryResult - update wikicode to align with expected behaviour * commons-app#3780 Create media using a combination of Entities & MwQueryResult - replace old site of thumbnail title with most relevant caption
1 parent 172dda9 commit 4ffe59c

37 files changed

+633
-1349
lines changed

app/src/main/java/fr/free/nrw/commons/Media.java

+130-392
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,51 @@
11
package fr.free.nrw.commons
22

33
import androidx.core.text.HtmlCompat
4-
import fr.free.nrw.commons.depictions.Media.DepictedImagesFragment
5-
import fr.free.nrw.commons.media.Depictions
4+
import fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_ID_PREFIX
5+
import fr.free.nrw.commons.media.IdAndCaptions
66
import fr.free.nrw.commons.media.MediaClient
77
import io.reactivex.Single
8-
import io.reactivex.functions.Function5
98
import timber.log.Timber
109
import javax.inject.Inject
1110
import javax.inject.Singleton
1211

1312
/**
1413
* Fetch additional media data from the network that we don't store locally.
1514
*
16-
* This includes things like category lists and multilingual descriptions,
17-
* which are not intrinsic to the media and may change due to editing.
15+
*
16+
* This includes things like category lists and multilingual descriptions, which are not intrinsic
17+
* to the media and may change due to editing.
1818
*/
1919
@Singleton
2020
class MediaDataExtractor @Inject constructor(private val mediaClient: MediaClient) {
2121

22-
/**
23-
* Simplified method to extract all details required to show media details.
24-
* It fetches media object, deletion status, talk page and captions for the filename
25-
* @param filename for which the details are to be fetched
26-
* @return full Media object with all details including deletion status and talk page
27-
*/
28-
fun fetchMediaDetails(filename: String, pageId: String?): Single<Media> {
29-
return Single.zip(
30-
getMediaFromFileName(filename),
31-
mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/$filename"),
32-
getDiscussion(filename),
33-
if (pageId != null)
34-
getCaption(DepictedImagesFragment.PAGE_ID_PREFIX + pageId)
35-
else Single.just(MediaClient.NO_CAPTION),
36-
getDepictions(filename),
37-
Function5 { media: Media, deletionStatus: Boolean, discussion: String, caption: String, depictions: Depictions ->
38-
combineToMedia(
39-
media,
40-
deletionStatus,
41-
discussion,
42-
caption,
43-
depictions
44-
)
22+
fun fetchDepictionIdsAndLabels(media: Media) =
23+
mediaClient.getEntities(media.depictionIds)
24+
.map {
25+
it.entities()
26+
.mapValues { entry -> entry.value.labels().mapValues { it.value.value() } }
4527
}
46-
)
47-
}
28+
.map { it.map { (key, value) -> IdAndCaptions(key, value) } }
29+
.onErrorReturn { emptyList() }
4830

49-
private fun combineToMedia(
50-
media: Media,
51-
deletionStatus: Boolean,
52-
discussion: String,
53-
caption: String,
54-
depictions: Depictions
55-
): Media {
56-
media.discussion = discussion
57-
media.caption = caption
58-
media.depictions = depictions
59-
if (deletionStatus) {
60-
media.isRequestedDeletion = true
61-
}
62-
return media
63-
}
31+
fun checkDeletionRequestExists(media: Media) =
32+
mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + media.filename)
6433

65-
/**
66-
* Obtains captions using filename
67-
* @param wikibaseIdentifier
68-
*
69-
* @return caption for the image in user's locale
70-
* Ex: "a nice painting" (english locale) and "No Caption" in case the caption is not available for the image
71-
*/
72-
private fun getCaption(wikibaseIdentifier: String): Single<String> {
73-
return mediaClient.getCaptionByWikibaseIdentifier(wikibaseIdentifier)
74-
}
75-
76-
/**
77-
* Fetch depictions from the MediaWiki API
78-
* @param filename the filename we will return the caption for
79-
* @return Depictions
80-
*/
81-
private fun getDepictions(filename: String): Single<Depictions> {
82-
return mediaClient.getDepictions(filename)
83-
.doOnError { throwable: Throwable? ->
84-
Timber.e(
85-
throwable,
86-
"error while fetching depictions"
87-
)
34+
fun fetchDiscussion(media: Media) =
35+
mediaClient.getPageHtml(media.filename.replace("File", "File talk"))
36+
.map { HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY).toString() }
37+
.onErrorReturn {
38+
Timber.d("Error occurred while fetching discussion")
39+
""
8840
}
89-
}
9041

91-
/**
92-
* Method can be used to fetch media for a given filename
93-
* @param filename Eg. File:Test.jpg
94-
* @return return data rich Media object
95-
*/
96-
fun getMediaFromFileName(filename: String?): Single<Media> {
97-
return mediaClient.getMedia(filename)
98-
}
42+
fun refresh(media: Media): Single<Media> {
43+
return Single.ambArray(
44+
mediaClient.getMediaById(PAGE_ID_PREFIX + media.pageId)
45+
.onErrorResumeNext { Single.never() },
46+
mediaClient.getMedia(media.filename)
47+
.onErrorResumeNext { Single.never() }
48+
)
9949

100-
/**
101-
* Fetch talk page from the MediaWiki API
102-
* @param filename
103-
* @return
104-
*/
105-
private fun getDiscussion(filename: String): Single<String> {
106-
return mediaClient.getPageHtml(filename.replace("File", "File talk"))
107-
.map { discussion: String? ->
108-
HtmlCompat.fromHtml(
109-
discussion!!,
110-
HtmlCompat.FROM_HTML_MODE_LEGACY
111-
).toString()
112-
}
113-
.onErrorReturn { throwable: Throwable? ->
114-
Timber.e(throwable, "Error occurred while fetching discussion")
115-
""
116-
}
11750
}
118-
11951
}

app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java

-32
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import static android.view.View.GONE;
44
import static android.view.View.VISIBLE;
5-
import static fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_ID_PREFIX;
65

76
import android.annotation.SuppressLint;
87
import android.os.Bundle;
@@ -253,37 +252,6 @@ private void handleSuccess(List<Media> collection) {
253252
progressBar.setVisibility(GONE);
254253
isLoading = false;
255254
statusTextView.setVisibility(GONE);
256-
for (Media m : collection) {
257-
final String pageId = m.getPageId();
258-
if (pageId != null) {
259-
replaceTitlesWithCaptions(PAGE_ID_PREFIX + pageId, mediaSize++);
260-
}
261-
}
262-
}
263-
264-
/**
265-
* fetch captions for the image using filename and replace title of on the image thumbnail(if captions are available)
266-
* else show filename
267-
*/
268-
public void replaceTitlesWithCaptions(String wikibaseIdentifier, int i) {
269-
compositeDisposable.add(mediaClient.getCaptionByWikibaseIdentifier(wikibaseIdentifier)
270-
.subscribeOn(Schedulers.io())
271-
.observeOn(AndroidSchedulers.mainThread())
272-
.subscribe(subscriber -> {
273-
handleLabelforImage(subscriber, i);
274-
}));
275-
276-
}
277-
278-
/**
279-
* If caption is available for the image, then modify grid adapter
280-
* to show captions
281-
*/
282-
private void handleLabelforImage(String s, int position) {
283-
if (!s.trim().equals(getString(R.string.detail_caption_empty))) {
284-
gridAdapter.getItem(position).setThumbnailTitle(s);
285-
gridAdapter.notifyDataSetChanged();
286-
}
287255
}
288256

289257
/**

app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public View getView(int position, View convertView, ViewGroup parent) {
8888
SimpleDraweeView imageView = convertView.findViewById(R.id.categoryImageView);
8989
TextView fileName = convertView.findViewById(R.id.categoryImageTitle);
9090
TextView author = convertView.findViewById(R.id.categoryImageAuthor);
91-
fileName.setText(item.getThumbnailTitle());
91+
fileName.setText(item.getMostRelevantCaption());
9292
setAuthorView(item, author);
9393
imageView.setImageURI(item.getThumbUrl());
9494
return convertView;

app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java

+55-55
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package fr.free.nrw.commons.contributions;
22

3+
import android.net.Uri;
34
import android.os.Parcel;
5+
import androidx.annotation.Nullable;
46
import androidx.room.Entity;
57
import fr.free.nrw.commons.Media;
68
import fr.free.nrw.commons.auth.SessionManager;
7-
import fr.free.nrw.commons.upload.UploadMediaDetail;
89
import fr.free.nrw.commons.upload.UploadItem;
10+
import fr.free.nrw.commons.upload.UploadMediaDetail;
911
import fr.free.nrw.commons.upload.WikidataPlace;
1012
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
1113
import java.util.ArrayList;
12-
import java.util.HashMap;
14+
import java.util.Date;
1315
import java.util.List;
14-
import java.util.Map;
1516
import java.util.Objects;
1617

1718
@Entity(tableName = "contribution")
@@ -34,24 +35,23 @@ public class Contribution extends Media {
3435
*/
3536
private List<DepictedItem> depictedItems = new ArrayList<>();
3637
private String mimeType;
37-
/**
38-
* This hasmap stores the list of multilingual captions, where key of the HashMap is the language
39-
* and value is the caption in the corresponding language Ex: key = "en", value: "<caption in
40-
* short in English>" key = "de" , value: "<caption in german>"
41-
*/
42-
private HashMap<String, String> captions = new HashMap<>();
38+
@Nullable
39+
private Uri localUri;
40+
private long dataLength;
41+
private Date dateCreated;
4342

4443
public Contribution() {
4544
}
4645

4746
public Contribution(final UploadItem item, final SessionManager sessionManager,
4847
final List<DepictedItem> depictedItems, final List<String> categories) {
49-
super(item.getMediaUri(),
48+
super(
5049
item.getFileName(),
51-
UploadMediaDetail.formatList(item.getUploadMediaDetails()),
50+
UploadMediaDetail.formatCaptions(item.getUploadMediaDetails()),
51+
UploadMediaDetail.formatDescriptions(item.getUploadMediaDetails()),
5252
sessionManager.getAuthorName(),
5353
categories);
54-
captions = new HashMap<>(UploadMediaDetail.formatCaptions(item.getUploadMediaDetails()));
54+
localUri = item.getMediaUri();
5555
decimalCoords = item.getGpsCoords().getDecimalCoords();
5656
dateCreatedSource = "";
5757
this.depictedItems = depictedItems;
@@ -117,24 +117,6 @@ public void setMimeType(final String mimeType) {
117117
this.mimeType = mimeType;
118118
}
119119

120-
/**
121-
* Captions are a feature part of Structured data. They are meant to store short, multilingual
122-
* descriptions about files This is a replacement of the previously used titles for images (titles
123-
* were not multilingual) Also now captions replace the previous convention of using title for
124-
* filename
125-
* <p>
126-
* key of the HashMap is the language and value is the caption in the corresponding language
127-
* <p>
128-
* returns list of captions stored in hashmap
129-
*/
130-
public HashMap<String, String> getCaptions() {
131-
return captions;
132-
}
133-
134-
public void setCaptions(HashMap<String, String> captions) {
135-
this.captions = captions;
136-
}
137-
138120
@Override
139121
public int describeContents() {
140122
return 0;
@@ -147,7 +129,6 @@ public void writeToParcel(final Parcel dest, final int flags) {
147129
dest.writeLong(transferred);
148130
dest.writeString(decimalCoords);
149131
dest.writeString(dateCreatedSource);
150-
dest.writeSerializable(captions);
151132
}
152133

153134
/**
@@ -156,13 +137,7 @@ public void writeToParcel(final Parcel dest, final int flags) {
156137
* @param state
157138
*/
158139
public Contribution(Media media, int state) {
159-
super(media.getPageId(),
160-
media.getLocalUri(), media.getThumbUrl(), media.getImageUrl(), media.getFilename(),
161-
media.getDescription(),
162-
media.getDiscussion(),
163-
media.getDataLength(), media.getDateCreated(), media.getDateUploaded(),
164-
media.getLicense(), media.getLicenseUrl(), media.getCreator(), media.getCategories(),
165-
media.isRequestedDeletion(), media.getCoordinates());
140+
super(media);
166141
this.state = state;
167142
}
168143

@@ -172,7 +147,6 @@ protected Contribution(final Parcel in) {
172147
transferred = in.readLong();
173148
decimalCoords = in.readString();
174149
dateCreatedSource = in.readString();
175-
captions = (HashMap<String, String>) in.readSerializable();
176150
}
177151

178152
public static final Creator<Contribution> CREATOR = new Creator<Contribution>() {
@@ -187,34 +161,60 @@ public Contribution[] newArray(final int size) {
187161
}
188162
};
189163

190-
/**
191-
* Equals implementation of Contributions that compares all parameters for checking equality
192-
*/
164+
@Nullable
165+
public Uri getLocalUri() {
166+
return localUri;
167+
}
168+
169+
public void setLocalUri(@Nullable Uri localUri) {
170+
this.localUri = localUri;
171+
}
172+
173+
public long getDataLength() {
174+
return dataLength;
175+
}
176+
177+
public void setDataLength(long dataLength) {
178+
this.dataLength = dataLength;
179+
}
180+
181+
public Date getDateCreated() {
182+
return dateCreated;
183+
}
184+
185+
public void setDateCreated(Date dateCreated) {
186+
this.dateCreated = dateCreated;
187+
}
188+
193189
@Override
194190
public boolean equals(final Object o) {
195191
if (this == o) {
196192
return true;
197193
}
198-
if (!(o instanceof Contribution)) {
194+
if (o == null || getClass() != o.getClass()) {
195+
return false;
196+
}
197+
if (!super.equals(o)) {
199198
return false;
200199
}
201200
final Contribution that = (Contribution) o;
202-
return getState() == that.getState() && getTransferred() == that.getTransferred() && Objects
203-
.equals(getDecimalCoords(), that.getDecimalCoords()) && Objects
204-
.equals(getDateCreatedSource(), that.getDateCreatedSource()) && Objects
205-
.equals(getWikidataPlace(), that.getWikidataPlace()) && Objects
206-
.equals(getDepictedItems(), that.getDepictedItems()) && Objects
207-
.equals(getMimeType(), that.getMimeType()) && Objects
208-
.equals(getCaptions(), that.getCaptions());
201+
return state == that.state &&
202+
transferred == that.transferred &&
203+
dataLength == that.dataLength &&
204+
Objects.equals(decimalCoords, that.decimalCoords) &&
205+
Objects.equals(dateCreatedSource, that.dateCreatedSource) &&
206+
Objects.equals(wikidataPlace, that.wikidataPlace) &&
207+
Objects.equals(depictedItems, that.depictedItems) &&
208+
Objects.equals(mimeType, that.mimeType) &&
209+
Objects.equals(localUri, that.localUri) &&
210+
Objects.equals(dateCreated, that.dateCreated);
209211
}
210212

211-
/**
212-
* Hash code implementation of contributions that considers all parameters for calculating hash.
213-
*/
214213
@Override
215214
public int hashCode() {
216215
return Objects
217-
.hash(getState(), getTransferred(), getDecimalCoords(), getDateCreatedSource(),
218-
getWikidataPlace(), getDepictedItems(), getMimeType(), getCaptions());
216+
.hash(super.hashCode(), state, transferred, decimalCoords, dateCreatedSource,
217+
wikidataPlace,
218+
depictedItems, mimeType, localUri, dataLength, dateCreated);
219219
}
220220
}

0 commit comments

Comments
 (0)