forked from MapComplete/MapComplete
Add wikipedia box
This commit is contained in:
parent
1edf829cad
commit
8b4442c8cc
20 changed files with 401 additions and 149 deletions
89
Logic/Web/Wikidata.ts
Normal file
89
Logic/Web/Wikidata.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
import {Utils} from "../../Utils";
|
||||
|
||||
|
||||
export interface WikidataResponse {
|
||||
|
||||
id: string,
|
||||
labels: Map<string, string>,
|
||||
descriptions: Map<string, string>,
|
||||
claims: Map<string, Set<string>>,
|
||||
wikisites: Map<string, string>
|
||||
commons: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility functions around wikidata
|
||||
*/
|
||||
export default class Wikidata {
|
||||
|
||||
private static ParseResponse(entity: any): WikidataResponse {
|
||||
const labels = new Map<string, string>()
|
||||
for (const labelName in entity.labels) {
|
||||
// The labelname is the language code
|
||||
labels.set(labelName, entity.labels[labelName].value)
|
||||
}
|
||||
|
||||
const descr = new Map<string, string>()
|
||||
for (const labelName in entity.descriptions) {
|
||||
// The labelname is the language code
|
||||
descr.set(labelName, entity.descriptions[labelName].value)
|
||||
}
|
||||
|
||||
const sitelinks = new Map<string, string>();
|
||||
for (const labelName in entity.sitelinks) {
|
||||
// labelName is `${language}wiki`
|
||||
const language = labelName.substring(0, labelName.length - 4)
|
||||
const title = entity.sitelinks[labelName].title
|
||||
sitelinks.set(language, title)
|
||||
}
|
||||
|
||||
const commons = sitelinks.get("commons")
|
||||
sitelinks.delete("commons")
|
||||
|
||||
const claims = new Map<string, Set<string>>();
|
||||
for (const claimId of entity.claims) {
|
||||
|
||||
const claimsList: any[] = entity.claims[claimId]
|
||||
const values = new Set<string>()
|
||||
for (const claim of claimsList) {
|
||||
const value = claim.mainsnak.datavalue.value;
|
||||
values.add(value)
|
||||
}
|
||||
claims.set(claimId, values);
|
||||
}
|
||||
|
||||
return {
|
||||
claims: claims,
|
||||
descriptions: descr,
|
||||
id: entity.id,
|
||||
labels: labels,
|
||||
wikisites: sitelinks,
|
||||
commons: commons
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a wikidata page
|
||||
* @returns the entity of the given value
|
||||
*/
|
||||
public static async LoadWikidataEntry(value: string | number): Promise<WikidataResponse> {
|
||||
const wikidataUrl = "https://www.wikidata.org/wiki/"
|
||||
if (typeof value === "number") {
|
||||
value = "Q" + value
|
||||
}
|
||||
if (value.startsWith(wikidataUrl)) {
|
||||
value = value.substring(wikidataUrl.length)
|
||||
}
|
||||
if (value.startsWith("http")) {
|
||||
// Probably some random link in the image field - we skip it
|
||||
return undefined
|
||||
}
|
||||
if (!value.startsWith("Q")) {
|
||||
value = "Q" + value
|
||||
}
|
||||
const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json";
|
||||
const response = await Utils.downloadJson(url)
|
||||
return Wikidata.ParseResponse(response.entities[value]);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,8 @@
|
|||
* Some usefull utility functions around the wikipedia API
|
||||
*/
|
||||
import {Utils} from "../../Utils";
|
||||
import WikipediaBox from "../../UI/WikipediaBox";
|
||||
import {UIEventSource} from "../UIEventSource";
|
||||
import Wikidata from "./Wikidata";
|
||||
|
||||
export default class Wikipedia {
|
||||
|
||||
|
@ -14,22 +15,36 @@ export default class Wikipedia {
|
|||
private static readonly classesToRemove = [
|
||||
"shortdescription",
|
||||
"sidebar",
|
||||
"infobox",
|
||||
"infobox","infobox_v2",
|
||||
"noprint",
|
||||
"ambox",
|
||||
"mw-editsection",
|
||||
"mw-selflink",
|
||||
"hatnote" // Often redirects
|
||||
]
|
||||
|
||||
public static async GetArticle(options: {
|
||||
private static readonly _cache = new Map<string, UIEventSource<{ success: string } | { error: any }>>()
|
||||
|
||||
public static GetArticle(options: {
|
||||
pageName: string,
|
||||
language?: "en" | string,
|
||||
section?: number,
|
||||
language?: "en" | string}): UIEventSource<{ success: string } | { error: any }>{
|
||||
const key = (options.language ?? "en")+":"+options.pageName
|
||||
const cached = Wikipedia._cache.get(key)
|
||||
if(cached !== undefined){
|
||||
return cached
|
||||
}
|
||||
const v = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticleAsync(options))
|
||||
Wikipedia._cache.set(key, v)
|
||||
return v;
|
||||
}
|
||||
|
||||
public static async GetArticleAsync(options: {
|
||||
pageName: string,
|
||||
language?: "en" | string
|
||||
}): Promise<string> {
|
||||
|
||||
let section = ""
|
||||
if (options.section !== undefined) {
|
||||
section = "§ion=" + options.section
|
||||
}
|
||||
const url = `https://${options.language ?? "en"}.wikipedia.org/w/api.php?action=parse${section}&format=json&origin=*&prop=text&page=` + options.pageName
|
||||
const language = options.language ?? "en"
|
||||
const url = `https://${language}.wikipedia.org/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + options.pageName
|
||||
const response = await Utils.downloadJson(url)
|
||||
const html = response["parse"]["text"]["*"];
|
||||
|
||||
|
@ -43,7 +58,19 @@ export default class Wikipedia {
|
|||
toRemoveElement.parentElement?.removeChild(toRemoveElement)
|
||||
}
|
||||
}
|
||||
return content.innerHTML;
|
||||
|
||||
const links = Array.from(content.getElementsByTagName("a"))
|
||||
|
||||
console.log("Links are", links)
|
||||
// Rewrite relative links to absolute links + open them in a new tab
|
||||
links.filter(link => link.getAttribute("href")?.startsWith("/") ?? false).
|
||||
forEach(link => {
|
||||
link.target = '_blank'
|
||||
// note: link.getAttribute("href") gets the textual value, link.href is the rewritten version which'll contain the host for relative paths
|
||||
link.href = `https://${language}.wikipedia.org${link.getAttribute("href")}`;
|
||||
})
|
||||
|
||||
return content.innerHTML
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue