diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts index 111eb6e397..3a597a6fa2 100644 --- a/Logic/Web/Wikidata.ts +++ b/Logic/Web/Wikidata.ts @@ -1,5 +1,6 @@ import {Utils} from "../../Utils"; import {UIEventSource} from "../UIEventSource"; +import * as wds from "wikibase-sdk" export class WikidataResponse { public readonly id: string @@ -63,22 +64,15 @@ export class WikidataResponse { } static extractClaims(claimsJson: any): Map> { + + const simplified = wds.simplify.claims(claimsJson, { + timeConverter: 'simple-day' + }) + const claims = new Map>(); - for (const claimId in claimsJson) { - - const claimsList: any[] = claimsJson[claimId] - const values = new Set() - for (const claim of claimsList) { - let value = claim.mainsnak?.datavalue?.value; - if (value === undefined) { - continue; - } - if (value.id !== undefined) { - value = value.id - } - values.add(value) - } - claims.set(claimId, values); + for (const claimId in simplified) { + const claimsList: any[] = simplified[claimId] + claims.set(claimId, new Set(claimsList)); } return claims } diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts index 703346bd3d..f9c8240d35 100644 --- a/Logic/Web/Wikipedia.ts +++ b/Logic/Web/Wikipedia.ts @@ -20,6 +20,7 @@ export default class Wikipedia { "ambox", "mw-editsection", "mw-selflink", + "mw-empty-elt", "hatnote" // Often redirects ] diff --git a/UI/Wikipedia/WikidataPreviewBox.ts b/UI/Wikipedia/WikidataPreviewBox.ts index feca815fa2..bc70f2fefe 100644 --- a/UI/Wikipedia/WikidataPreviewBox.ts +++ b/UI/Wikipedia/WikidataPreviewBox.ts @@ -12,63 +12,66 @@ import {WikimediaImageProvider} from "../../Logic/ImageProviders/WikimediaImageP import Link from "../Base/Link"; import Svg from "../../Svg"; import BaseUIElement from "../BaseUIElement"; +import {Utils} from "../../Utils"; export default class WikidataPreviewBox extends VariableUiElement { - - constructor(wikidataId : UIEventSource) { + + constructor(wikidataId: UIEventSource) { let inited = false; const wikidata = wikidataId .stabilized(250) .bind(id => { - if (id === undefined || id === "" || id === "Q") { - return null; - } - inited = true; - return Wikidata.LoadWikidataEntry(id) - }) - + if (id === undefined || id === "" || id === "Q") { + return null; + } + inited = true; + return Wikidata.LoadWikidataEntry(id) + }) + super(wikidata.map(maybeWikidata => { - if(maybeWikidata === null || !inited){ + if (maybeWikidata === null || !inited) { return undefined; } - - if(maybeWikidata === undefined){ + + if (maybeWikidata === undefined) { return new Loading(Translations.t.general.loading) } - + if (maybeWikidata["error"] !== undefined) { return new FixedUiElement(maybeWikidata["error"]).SetClass("alert") } - const wikidata = maybeWikidata["success"] + const wikidata = maybeWikidata["success"] return WikidataPreviewBox.WikidataResponsePreview(wikidata) })) - + } - - public static WikidataResponsePreview(wikidata: WikidataResponse): BaseUIElement{ + + public static WikidataResponsePreview(wikidata: WikidataResponse): BaseUIElement { let link = new Link( new Combine([ wikidata.id, Svg.wikidata_ui().SetStyle("width: 2.5rem").SetClass("block") - ]).SetClass("flex"), - Wikidata.IdToArticle(wikidata.id) ,true).SetClass("must-link") - - let info = new Combine( [ - new Combine([Translation.fromMap(wikidata.labels).SetClass("font-bold"), + ]).SetClass("flex"), + Wikidata.IdToArticle(wikidata.id), true).SetClass("must-link") + + let info = new Combine([ + new Combine( + [Translation.fromMap(wikidata.labels).SetClass("font-bold"), link]).SetClass("flex justify-between"), - Translation.fromMap(wikidata.descriptions) + Translation.fromMap(wikidata.descriptions), + WikidataPreviewBox.QuickFacts(wikidata) ]).SetClass("flex flex-col link-underline") let imageUrl = undefined - if(wikidata.claims.get("P18")?.size > 0){ + if (wikidata.claims.get("P18")?.size > 0) { imageUrl = Array.from(wikidata.claims.get("P18"))[0] } - if(imageUrl){ - imageUrl = WikimediaImageProvider.singleton.PrepUrl(imageUrl).url - info = new Combine([ new Img(imageUrl).SetStyle("max-width: 5rem; width: unset; height: 4rem").SetClass("rounded-xl mr-2"), + if (imageUrl) { + imageUrl = WikimediaImageProvider.singleton.PrepUrl(imageUrl).url + info = new Combine([new Img(imageUrl).SetStyle("max-width: 5rem; width: unset; height: 4rem").SetClass("rounded-xl mr-2"), info.SetClass("w-full")]).SetClass("flex") } @@ -76,5 +79,90 @@ export default class WikidataPreviewBox extends VariableUiElement { return info } - + + private static isHuman = [ + {p: 31/*is a*/, q: 5 /* human */}, + ] + // @ts-ignore + // @ts-ignore + private static extraProperties: { + requires?: { p: number, q?: number }[], + property: string, + display: Translation | Map BaseUIElement) /*If translation: Subs({value: * }) */> + }[] = [ + { + requires: WikidataPreviewBox.isHuman, + property: "P21", + display: new Map([ + ['Q6581097', () => Svg.gender_male_ui().SetStyle("width: 1rem; height: auto")], + ['Q6581072', () => Svg.gender_female_ui().SetStyle("width: 1rem; height: auto")], + ['Q1097630',() => Svg.gender_inter_ui().SetStyle("width: 1rem; height: auto")], + ['Q1052281',() => Svg.gender_trans_ui().SetStyle("width: 1rem; height: auto")/*'transwomen'*/], + ['Q2449503',() => Svg.gender_trans_ui().SetStyle("width: 1rem; height: auto")/*'transmen'*/], + ['Q48270',() => Svg.gender_queer_ui().SetStyle("width: 1rem; height: auto")] + ]) + }, + { + property: "P569", + requires: WikidataPreviewBox.isHuman, + display: new Translation({ + "*":"Born: {value}" + }) + }, + { + property: "P570", + requires: WikidataPreviewBox.isHuman, + display: new Translation({ + "*":"Died: {value}" + }) + } + ] + + public static QuickFacts(wikidata: WikidataResponse): BaseUIElement { + + const els : BaseUIElement[] = [] + for (const extraProperty of WikidataPreviewBox.extraProperties) { + let hasAllRequirements =true + for (const requirement of extraProperty.requires) { + if(!wikidata.claims.has("P"+requirement.p)){ + hasAllRequirements = false; + break + } + if(!wikidata.claims.get("P"+requirement.p).has("Q"+requirement.q)){ + hasAllRequirements = false; + break + } + } + if(!hasAllRequirements){ + continue + } + + const key = extraProperty.property + const display = extraProperty.display + const value: string[] = Array.from(wikidata.claims.get(key)) + if(value === undefined){ + continue + } + if(display instanceof Translation){ + els.push(display.Subs({value: value.join(", ")}).SetClass("m-2")) + continue + } + console.log("Display:", display, "key:",key) + const constructors = Utils.NoNull(value.map(property => display.get(property))) + const elems = constructors.map(v => { + if(typeof v === "string"){ + return new FixedUiElement(v) + }else{ + return v(); + } + }) + els.push(new Combine(elems).SetClass("flex m-2")) + } + if(els.length === 0){ + return undefined; + } + + return new Combine(els).SetClass("flex") + } + } \ No newline at end of file diff --git a/UI/Wikipedia/WikipediaBox.ts b/UI/Wikipedia/WikipediaBox.ts index 4c7ba92353..28fd62dc7e 100644 --- a/UI/Wikipedia/WikipediaBox.ts +++ b/UI/Wikipedia/WikipediaBox.ts @@ -182,6 +182,7 @@ export default class WikipediaBox extends Combine { language: language }) const wp = Translations.t.general.wikipedia + const quickFacts = WikidataPreviewBox.QuickFacts(wikidata); const contents: UIEventSource = htmlContent.map(htmlContent => { if (htmlContent === undefined) { // Still loading @@ -198,7 +199,9 @@ export default class WikipediaBox extends Combine { return undefined }) - return new Combine([new VariableUiElement(contents) + return new Combine([ + quickFacts?.SetClass("border-2 border-grey rounded-lg m-1 mb-0"), + new VariableUiElement(contents) .SetClass("block pl-6 pt-2")]) } diff --git a/assets/svg/gender_bi.svg b/assets/svg/gender_bi.svg new file mode 100644 index 0000000000..9ae9cf5c19 --- /dev/null +++ b/assets/svg/gender_bi.svg @@ -0,0 +1,10 @@ + +Created with Fabric.js 1.7.22 + + + + + + + + \ No newline at end of file diff --git a/assets/svg/gender_female.svg b/assets/svg/gender_female.svg new file mode 100644 index 0000000000..6af923135d --- /dev/null +++ b/assets/svg/gender_female.svg @@ -0,0 +1,10 @@ + +Created with Fabric.js 1.7.22 + + + + + + + + \ No newline at end of file diff --git a/assets/svg/gender_inter.svg b/assets/svg/gender_inter.svg new file mode 100644 index 0000000000..95f3932de4 --- /dev/null +++ b/assets/svg/gender_inter.svg @@ -0,0 +1,10 @@ + +Created with Fabric.js 1.7.22 + + + + + + + + \ No newline at end of file diff --git a/assets/svg/gender_male.svg b/assets/svg/gender_male.svg new file mode 100644 index 0000000000..b70e6e50ac --- /dev/null +++ b/assets/svg/gender_male.svg @@ -0,0 +1,10 @@ + +Created with Fabric.js 1.7.22 + + + + + + + + \ No newline at end of file diff --git a/assets/svg/gender_queer.svg b/assets/svg/gender_queer.svg new file mode 100644 index 0000000000..c8bcf16b72 --- /dev/null +++ b/assets/svg/gender_queer.svg @@ -0,0 +1,10 @@ + +Created with Fabric.js 1.7.22 + + + + + + + + \ No newline at end of file diff --git a/assets/svg/gender_trans.svg b/assets/svg/gender_trans.svg new file mode 100644 index 0000000000..d39540182a --- /dev/null +++ b/assets/svg/gender_trans.svg @@ -0,0 +1,10 @@ + +Created with Fabric.js 1.7.22 + + + + + + + + \ No newline at end of file diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 4d701f6781..4ac61d303e 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -679,6 +679,90 @@ "authors": [], "sources": [] }, + { + "path": "gender_bi.svg", + "license": "CC0", + "authors": [ + "Gender Icon Pack" + ], + "sources": [ + "https://www.iconpacks.net/free-icon-pack/gender-107.html" + ] + }, + { + "path": "gender_female.svg", + "license": "CC0", + "authors": [], + "sources": [] + }, + { + "path": "gender_female.svg", + "license": "CC0", + "authors": [ + "Gender Icon Pack" + ], + "sources": [ + "https://www.iconpacks.net/free-icon-pack/gender-107.html" + ] + }, + { + "path": "gender_inter.svg", + "license": "CC0", + "authors": [ + "Gender Icon Pack" + ], + "sources": [ + "https://www.iconpacks.net/free-icon-pack/gender-107.html" + ] + }, + { + "path": "gender_intersekse.svg", + "license": "CC0", + "authors": [], + "sources": [] + }, + { + "path": "gender_male.svg", + "license": "CC0", + "authors": [], + "sources": [] + }, + { + "path": "gender_male.svg", + "license": "CC0", + "authors": [ + "Gender Icon Pack" + ], + "sources": [ + "https://www.iconpacks.net/free-icon-pack/gender-107.html" + ] + }, + { + "path": "gender_queer.svg", + "license": "CC0", + "authors": [ + "Gender Icon Pack" + ], + "sources": [ + "https://www.iconpacks.net/free-icon-pack/gender-107.html" + ] + }, + { + "path": "gender_trans.svg", + "license": "CC0", + "authors": [], + "sources": [] + }, + { + "path": "gender_trans.svg", + "license": "CC0", + "authors": [ + "Gender Icon Pack" + ], + "sources": [ + "https://www.iconpacks.net/free-icon-pack/gender-107.html" + ] + }, { "path": "hand.svg", "license": "CC0", diff --git a/package-lock.json b/package-lock.json index d3613fdaa2..a21cb12e71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@types/leaflet.markercluster": "^1.4.3", "@types/lz-string": "^1.3.34", "@types/prompt-sync": "^4.1.0", + "@types/wikidata-sdk": "^6.1.0", "country-language": "^0.1.7", "email-validator": "^2.0.4", "escape-html": "^1.0.3", @@ -44,7 +45,9 @@ "parcel": "^1.2.4", "prompt-sync": "^4.2.0", "tailwindcss": "^2.2.15", - "tslint": "^6.1.3" + "tslint": "^6.1.3", + "wikibase-sdk": "^7.14.0", + "wikidata-sdk": "^7.14.0" }, "devDependencies": { "@babel/polyfill": "^7.10.4", @@ -3024,6 +3027,15 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, + "node_modules/@types/wikidata-sdk": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/wikidata-sdk/-/wikidata-sdk-6.1.0.tgz", + "integrity": "sha512-4IG0mbD97vnpNUCCsybYOZvXDPOgv4riDHllKKkv4yKM90md2j2lH3yqp2pnPURHS79tbQibZybANHT++J25fA==", + "deprecated": "This is a stub types definition. wikidata-sdk provides its own type definitions, so you do not need this installed.", + "dependencies": { + "wikidata-sdk": "*" + } + }, "node_modules/abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -17243,6 +17255,25 @@ "string-width": "^1.0.2 || 2" } }, + "node_modules/wikibase-sdk": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/wikibase-sdk/-/wikibase-sdk-7.14.0.tgz", + "integrity": "sha512-9IcgqQ5CCNiOl5Xb/SdWdijvVxGh/9eCWvX5OeCgHKE0xeHHngAcuketBM8YBAbTUDKhFwbThPjxV/zl0Qr6gg==", + "engines": { + "node": ">= 6.4" + } + }, + "node_modules/wikidata-sdk": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/wikidata-sdk/-/wikidata-sdk-7.14.0.tgz", + "integrity": "sha512-2B2TyyLAxfc1qHFSoKL+x1iQwXgrixQPopf1dRF0qdbvB1FFKOUP5cbSEhGH3pJ1Bos63o7bpN0P8voJ8DEs9Q==", + "dependencies": { + "wikibase-sdk": "^7.14.0" + }, + "engines": { + "node": ">= 6.4" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -20114,6 +20145,14 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, + "@types/wikidata-sdk": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/wikidata-sdk/-/wikidata-sdk-6.1.0.tgz", + "integrity": "sha512-4IG0mbD97vnpNUCCsybYOZvXDPOgv4riDHllKKkv4yKM90md2j2lH3yqp2pnPURHS79tbQibZybANHT++J25fA==", + "requires": { + "wikidata-sdk": "*" + } + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -31601,6 +31640,19 @@ "string-width": "^1.0.2 || 2" } }, + "wikibase-sdk": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/wikibase-sdk/-/wikibase-sdk-7.14.0.tgz", + "integrity": "sha512-9IcgqQ5CCNiOl5Xb/SdWdijvVxGh/9eCWvX5OeCgHKE0xeHHngAcuketBM8YBAbTUDKhFwbThPjxV/zl0Qr6gg==" + }, + "wikidata-sdk": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/wikidata-sdk/-/wikidata-sdk-7.14.0.tgz", + "integrity": "sha512-2B2TyyLAxfc1qHFSoKL+x1iQwXgrixQPopf1dRF0qdbvB1FFKOUP5cbSEhGH3pJ1Bos63o7bpN0P8voJ8DEs9Q==", + "requires": { + "wikibase-sdk": "^7.14.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index d353002ed6..f480e7dc00 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@types/leaflet.markercluster": "^1.4.3", "@types/lz-string": "^1.3.34", "@types/prompt-sync": "^4.1.0", + "@types/wikidata-sdk": "^6.1.0", "country-language": "^0.1.7", "email-validator": "^2.0.4", "escape-html": "^1.0.3", @@ -91,7 +92,9 @@ "parcel": "^1.2.4", "prompt-sync": "^4.2.0", "tailwindcss": "^2.2.15", - "tslint": "^6.1.3" + "tslint": "^6.1.3", + "wikibase-sdk": "^7.14.0", + "wikidata-sdk": "^7.14.0" }, "devDependencies": { "@babel/polyfill": "^7.10.4", diff --git a/test.ts b/test.ts index bb4fc949af..a8cc94f929 100644 --- a/test.ts +++ b/test.ts @@ -1,5 +1,13 @@ -import FeaturedMessage from "./UI/BigComponents/FeaturedMessage"; -import Combine from "./UI/Base/Combine"; +import * as wd from "wikidata-sdk" +import * as wds from "wikibase-sdk" +import {Utils} from "./Utils"; -new FeaturedMessage().AttachTo("maindiv") -new Combine(FeaturedMessage.WelcomeMessages().map(wm => FeaturedMessage.CreateFeaturedBox(wm))).AttachTo("extradiv") \ No newline at end of file +const url = wd.getEntities(["Q42"]) +console.log(url) +Utils.downloadJson(url).then(async (entities) => { + //const parsed = wd.parse.wb.entities(entities)["Q42"] + console.log(entities) + console.log(wds.simplify.entity(entities.entities["Q42"], { + timeConverter: 'simple-day' + })) +}) \ No newline at end of file