forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			116 lines
		
	
	
		
			No EOL
		
	
	
		
			3.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			No EOL
		
	
	
		
			3.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import {Utils} from "../../Utils";
 | 
						|
import {UIEventSource} from "../UIEventSource";
 | 
						|
 | 
						|
 | 
						|
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
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private static readonly _cache = new Map<number, UIEventSource<{success: WikidataResponse} | {error: any}>>()
 | 
						|
    public static LoadWikidataEntry(value: string | number): UIEventSource<{success: WikidataResponse} | {error: any}> {
 | 
						|
        const key = this.ExtractKey(value)
 | 
						|
        const cached = Wikidata._cache.get(key)
 | 
						|
        if(cached !== undefined){
 | 
						|
            return cached
 | 
						|
        }
 | 
						|
        const src = UIEventSource.FromPromiseWithErr(Wikidata.LoadWikidataEntryAsync(key))
 | 
						|
        Wikidata._cache.set(key, src)
 | 
						|
        return src;
 | 
						|
    }
 | 
						|
    
 | 
						|
    private static ExtractKey(value: string | number) : number{
 | 
						|
        if (typeof value === "number") {
 | 
						|
           return value
 | 
						|
        }
 | 
						|
        const wikidataUrl = "https://www.wikidata.org/wiki/"
 | 
						|
        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 = value.substring(1)
 | 
						|
        }
 | 
						|
        const n = Number(value)
 | 
						|
        if(isNaN(n)){
 | 
						|
            return undefined
 | 
						|
        }
 | 
						|
        return n;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Loads a wikidata page
 | 
						|
     * @returns the entity of the given value
 | 
						|
     */
 | 
						|
    public static async LoadWikidataEntryAsync(value: string | number): Promise<WikidataResponse> {
 | 
						|
        const id = Wikidata.ExtractKey(value)
 | 
						|
        if(id === undefined){
 | 
						|
            console.warn("Could not extract a wikidata entry from", value)
 | 
						|
            return undefined;
 | 
						|
        }
 | 
						|
        console.log("Requesting wikidata with id", id)
 | 
						|
        const url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json";
 | 
						|
        const response = await Utils.downloadJson(url)
 | 
						|
        return Wikidata.ParseResponse(response.entities["Q" + id])
 | 
						|
    }
 | 
						|
 | 
						|
} |