Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
HarelM committed Dec 17, 2024
2 parents 0221689 + 07ff913 commit 9af37ef
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 23 deletions.
6 changes: 3 additions & 3 deletions IsraelHiking.Web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe("ImageAttributionService", () => {
expect(response.url).toBe("https://www.example.com");
}));

it("should fetch data from wikipedia when getting wikimedia image", inject([ImageAttributionService, HttpTestingController],
it("should fetch data from wikimedia when getting wikimedia image", inject([ImageAttributionService, HttpTestingController],
async (service: ImageAttributionService, mockBackend: HttpTestingController) => {
const promise = service.getAttributionForImage("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/IHM_Image.jpeg");
mockBackend.match(r => r.url.startsWith("https://commons.wikimedia.org/"))[0].flush({
Expand All @@ -59,10 +59,88 @@ describe("ImageAttributionService", () => {
expect(response.url).toBe("https://commons.wikimedia.org/wiki/File:IHM_Image.jpeg");
}));

it("should remove html tags and get the value inside", inject([ImageAttributionService, HttpTestingController],
it("should fetch attribution from wikimedia when getting wikimedia image with attribution and no author", inject([ImageAttributionService, HttpTestingController],
async (service: ImageAttributionService, mockBackend: HttpTestingController) => {
const promise = service.getAttributionForImage("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/IHM_Image.jpeg");
mockBackend.match(r => r.url.startsWith("https://commons.wikimedia.org/"))[0].flush({
query: {
pages: {
"-1": {
imageinfo: [{
extmetadata: {
Attribution: {
value: "hello"
}
}
}]
}
}
}
});

const response = await promise;

expect(response).not.toBeNull();
expect(response.author).toBe("hello");
expect(response.url).toBe("https://commons.wikimedia.org/wiki/File:IHM_Image.jpeg");
}));

it("should fetch attribution from wikimedia when getting wikimedia image with permissive license and no author or attribution", inject([ImageAttributionService, HttpTestingController],
async (service: ImageAttributionService, mockBackend: HttpTestingController) => {
const promise = service.getAttributionForImage("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/IHM_Image.jpeg");
mockBackend.match(r => r.url.startsWith("https://commons.wikimedia.org/"))[0].flush({
query: {
pages: {
"14686480": {
imageinfo: [{
extmetadata: {
LicenseShortName: {
value: "Cc-by-sa-3.0"
}
}
}]
}
}
}
});

const response = await promise;

expect(response).not.toBeNull();
expect(response.author).toBe("Unknown");
expect(response.url).toBe("https://commons.wikimedia.org/wiki/File:IHM_Image.jpeg");
}));

it("should fetch data from wikimedia when getting wikimedia file", inject([ImageAttributionService, HttpTestingController],
async (service: ImageAttributionService, mockBackend: HttpTestingController) => {
const promise = service.getAttributionForImage("File:123.jpeg");
mockBackend.match(r => r.url.startsWith("https://commons.wikimedia.org/"))[0].flush({
query: {
pages: {
"-1": {
imageinfo: [{
extmetadata: {
Artist: {
value: "hello"
}
}
}]
}
}
}
});

const response = await promise;

expect(response).not.toBeNull();
expect(response.author).toBe("hello");
expect(response.url).toBe("https://commons.wikimedia.org/wiki/File:123.jpeg");
}));

it("should remove html tags and get the value inside", inject([ImageAttributionService, HttpTestingController],
async (service: ImageAttributionService, mockBackend: HttpTestingController) => {
const promise = service.getAttributionForImage("https://upload.wikimedia.org/wikipedia/he/thumb/a/a1/IHM_Image.jpeg");
mockBackend.match(r => r.url.startsWith("https://he.wikipedia.org/"))[0].flush({
query: {
pages: {
"-1": {
Expand All @@ -82,7 +160,7 @@ describe("ImageAttributionService", () => {

expect(response).not.toBeNull();
expect(response.author).toBe("hello");
expect(response.url).toBe("https://commons.wikimedia.org/wiki/File:IHM_Image.jpeg");
expect(response.url).toBe("https://he.wikipedia.org/wiki/File:IHM_Image.jpeg");
}));

it("should remove html tags, tabs and get the value inside", inject([ImageAttributionService, HttpTestingController],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HttpClient } from "@angular/common/http";
import { inject, Injectable } from "@angular/core";
import { firstValueFrom, timeout } from "rxjs";
import type { WikiPage } from "./wikidata.service";

export type ImageAttribution = {
author: string;
Expand All @@ -27,10 +28,11 @@ export class ImageAttributionService {
return this.attributionImageCache.get(imageUrl);
}
const url = new URL(imageUrl);
if (!url.hostname) {
const wikidataFileUrl = imageUrl.startsWith("File:");
if (!url.hostname && !wikidataFileUrl) {
return null;
}
if (!url.hostname.includes("upload.wikimedia")) {
if (!url.hostname.includes("upload.wikimedia") && !wikidataFileUrl) {
const imageAttribution = {
author: url.origin,
url: url.origin
Expand All @@ -39,21 +41,37 @@ export class ImageAttributionService {
return imageAttribution;
}

const imageName = imageUrl.split("/").pop();
const address = `https://commons.wikimedia.org/w/api.php?action=query&prop=imageinfo&iiprop=extmetadata&format=json&origin=*` +
`&titles=File:${imageName}`;
const imageName = imageUrl.split("/").pop().replace(/^File:/, "");
let wikiPrefix = "https://commons.wikimedia.org/";
const languageMatch = imageUrl.match(/https:\/\/upload\.wikimedia\.org\/wikipedia\/(.*?)\//);
if (languageMatch && languageMatch[1] !== "commons") {
wikiPrefix = `https://${languageMatch[1]}.wikipedia.org/`;
}
const address = `${wikiPrefix}w/api.php?action=query&prop=imageinfo&iiprop=extmetadata&format=json&origin=*&titles=File:${imageName}`;
try {
const response: any = await firstValueFrom(this.httpClient.get(address).pipe(timeout(3000)));
const extmetadata = response.query.pages[Object.keys(response.query.pages)[0]].imageinfo[0].extmetadata;
if (extmetadata?.Artist.value) {
const author = this.extractPlainText(extmetadata.Artist.value as string);
const response = await firstValueFrom(this.httpClient.get(address).pipe(timeout(3000))) as unknown as WikiPage;
const pagesIds = Object.keys(response.query.pages);
if (pagesIds.length === 0) {
return null;
}
const extmetadata = response.query.pages[pagesIds[0]].imageinfo[0].extmetadata;
const attribution = extmetadata?.Artist?.value || extmetadata?.Attribution?.value;
if (attribution) {
const author = this.extractPlainText(attribution);
const imageAttribution = {
author,
url: `https://commons.wikimedia.org/wiki/File:${imageName}`
url: `${wikiPrefix}wiki/File:${imageName}`
};
this.attributionImageCache.set(imageUrl, imageAttribution);
return imageAttribution;
}
const licenseLower = extmetadata?.LicenseShortName?.value.toLowerCase() || "";
if ((licenseLower.includes("cc") && !licenseLower.includes("nc")) || licenseLower.includes("public domain")) {
return {
author: "Unknown",
url: `${wikiPrefix}wiki/File:${imageName}`
};
}
} catch {} // eslint-disable-line
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion IsraelHiking.Web/src/application/services/poi.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ export class PoiService {
let imagesUrls = Object.keys(feature.properties)
.filter(k => k.startsWith("image"))
.map(k => feature.properties[k])
.filter(u => u.includes("wikimedia.org") || u.includes("inature.info") || u.includes("nakeb.co.il") || u.includes("jeepolog.com"));
.filter((u: string) => u.startsWith("File:") || u.includes("wikimedia.org") || u.includes("inature.info") || u.includes("nakeb.co.il") || u.includes("jeepolog.com"));
const imageAttributions = await Promise.all(imagesUrls.map(u => this.imageAttributinoService.getAttributionForImage(u)));
imagesUrls = imagesUrls.filter((_, i) => imageAttributions[i] != null);
return {
Expand Down
33 changes: 27 additions & 6 deletions IsraelHiking.Web/src/application/services/wikidata.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,29 @@ type WikiDataPage = {
statements: { [key: string]: { value: { content: any } }[] };
}

type WikipediaPage = {
export type WikiPage = {
query: {
pages: {
[key: string]: {
extract: string
extract: string,
original?: {
source: string
};
imageinfo: {
extmetadata: {
Artist?: {
value: string;
};
Attribution?: {
value: string;
};
LicenseShortName?: {
value: string;
}
};
}[];
}
}
},
}
}

Expand Down Expand Up @@ -73,10 +89,15 @@ export class WikidataService {
const indexString = GeoJSONUtils.setProperty(feature, "website", `https://${language}.wikipedia.org/wiki/${title}`);
feature.properties["poiSourceImageUrl" + indexString] = "https://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/128px-Wikipedia-logo-v2.svg.png";
}
const wikipediaPage = await firstValueFrom(this.httpClient.get(`https://${language}.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro=&explaintext=&titles=${title}&origin=*`).pipe(timeout(3000))) as unknown as WikipediaPage;
const wikipediaPage = await firstValueFrom(this.httpClient.get(`https://${language}.wikipedia.org/w/api.php?format=json&action=query&prop=extracts|pageimages&piprop=original&exintro=&explaintext=&titles=${title}&origin=*`).pipe(timeout(3000))) as unknown as WikiPage;
const pagesIds = Object.keys(wikipediaPage.query.pages);
if (pagesIds.length > 0) {
feature.properties.poiExternalDescription = wikipediaPage.query.pages[pagesIds[0]].extract;
if (pagesIds.length === 0) {
return;
}
const page = wikipediaPage.query.pages[pagesIds[0]];
feature.properties.poiExternalDescription = page.extract;
if (page.original?.source) {
GeoJSONUtils.setProperty(feature, "image", page.original.source);
}
}

Expand Down

0 comments on commit 9af37ef

Please sign in to comment.