vis-chronicle 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -12
- package/package.json +34 -30
- package/src/fetch.js +131 -272
- package/src/global-data.json +34 -4
- package/src/index.js +7 -0
- package/src/render.js +552 -0
- package/src/sparql-builder.js +25 -8
- package/src/wikidata.js +67 -13
- package/styles/style.css +22 -5
package/src/wikidata.js
CHANGED
|
@@ -88,6 +88,18 @@ const wikidata = module.exports = {
|
|
|
88
88
|
const contents = await fs.promises.readFile(path)
|
|
89
89
|
this.inputSpec = JSON.parse(contents)
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
// assign default values
|
|
93
|
+
if (!this.inputSpec.chronicle)
|
|
94
|
+
this.inputSpec.chronicle = {}
|
|
95
|
+
|
|
96
|
+
const chronicleOptions = this.inputSpec.chronicle
|
|
97
|
+
if (chronicleOptions.defaultLabel === undefined)
|
|
98
|
+
chronicleOptions.defaultLabel = '<a target="_blank" href="https://www.wikidata.org/wiki/{_QID}">{_LABEL}</a>'
|
|
99
|
+
if (chronicleOptions.maxUncertainTimePrecision === undefined)
|
|
100
|
+
chronicleOptions.maxUncertainTimePrecision = 10
|
|
101
|
+
if (chronicleOptions.shareSuccessiveUncertainty === undefined)
|
|
102
|
+
chronicleOptions.shareSuccessiveUncertainty = true
|
|
91
103
|
},
|
|
92
104
|
|
|
93
105
|
readCache: async function()
|
|
@@ -279,20 +291,29 @@ const wikidata = module.exports = {
|
|
|
279
291
|
// runs a SPARQL query for time values on an item or set of items
|
|
280
292
|
runTimeQueryTerm: async function (queryTermStr, items)
|
|
281
293
|
{
|
|
294
|
+
// keys that may appear on the query term that provide terms
|
|
295
|
+
const termTimeKeys = [
|
|
296
|
+
"general",
|
|
297
|
+
"start", "start_min", "start_max",
|
|
298
|
+
"end", "end_min", "end_max",
|
|
299
|
+
"value", "min", "max"
|
|
300
|
+
]
|
|
301
|
+
const termOtherKeys = [ "previous", "next" ]
|
|
302
|
+
|
|
282
303
|
const entityVarName = '_entity'
|
|
283
304
|
const entityVar = `?${entityVarName}`
|
|
284
305
|
const propVar = '?_prop'
|
|
285
306
|
const rankVar = '?_rank'
|
|
286
307
|
|
|
287
|
-
const queryBuilder = new SparqlBuilder()
|
|
288
|
-
queryBuilder.addCacheBuster(this.cacheBuster)
|
|
289
|
-
|
|
290
308
|
// create a dummy item representing the collective items
|
|
291
309
|
//TODO: validate that they match
|
|
292
310
|
item = { ...items[0] }
|
|
293
311
|
item.entity = entityVar
|
|
294
312
|
item.id = "DUMMY"
|
|
295
313
|
|
|
314
|
+
const queryBuilder = new SparqlBuilder()
|
|
315
|
+
queryBuilder.addCacheBuster(item.cacheBuster ? item.cacheBuster : this.cacheBuster)
|
|
316
|
+
|
|
296
317
|
queryTerm = this.getValueQueryTerm(queryTermStr, item)
|
|
297
318
|
|
|
298
319
|
// assembly query targets
|
|
@@ -302,21 +323,32 @@ const wikidata = module.exports = {
|
|
|
302
323
|
targetEntities.add(`wd:${item.entity}`)
|
|
303
324
|
}
|
|
304
325
|
queryBuilder.addQueryTerm(`VALUES ${entityVar}{${[...targetEntities].join(' ')}}`)
|
|
305
|
-
queryBuilder.addOutParam(entityVar)
|
|
326
|
+
queryBuilder.addOutParam(entityVar, { groupBy: true })
|
|
306
327
|
|
|
307
|
-
|
|
328
|
+
// Group by prop object so that multiple prev/next values on the same property are grouped,
|
|
329
|
+
// but different properties with different prev/next values are separate.
|
|
330
|
+
queryBuilder.addGroupParam(propVar)
|
|
331
|
+
|
|
332
|
+
queryBuilder.addOutParam(rankVar, { groupBy: true })
|
|
308
333
|
if (queryTerm.general)
|
|
309
334
|
{
|
|
310
335
|
queryBuilder.addQueryTerm(queryTerm.general)
|
|
311
336
|
}
|
|
312
337
|
if (queryTerm.value) //TODO: could unify better with loop below?
|
|
313
338
|
{
|
|
314
|
-
queryBuilder.addTimeTerm(queryTerm.value, "?_value", "?_value_ti", "?_value_pr")
|
|
339
|
+
queryBuilder.addTimeTerm(queryTerm.value, "?_value", "?_value_ti", "?_value_pr", { groupBy: true })
|
|
315
340
|
}
|
|
316
|
-
for (const termKey
|
|
341
|
+
for (const termKey of termTimeKeys)
|
|
317
342
|
{
|
|
343
|
+
if (!queryTerm[termKey]) continue
|
|
318
344
|
if (termKey == "general" || termKey == "value") continue
|
|
319
|
-
queryBuilder.addOptionalTimeTerm(queryTerm[termKey], `?_${termKey}_value`, `?_${termKey}_ti`, `?_${termKey}_pr
|
|
345
|
+
queryBuilder.addOptionalTimeTerm(queryTerm[termKey], `?_${termKey}_value`, `?_${termKey}_ti`, `?_${termKey}_pr`, { groupBy: true })
|
|
346
|
+
}
|
|
347
|
+
for (const termKey of termOtherKeys)
|
|
348
|
+
{
|
|
349
|
+
if (!queryTerm[termKey]) continue
|
|
350
|
+
queryBuilder.addOutParam(`(GROUP_CONCAT(DISTINCT ?_${termKey}_value; SEPARATOR=";") AS ?_${termKey}_out)`)
|
|
351
|
+
queryBuilder.addOptionalQueryTerm(queryTerm[termKey])
|
|
320
352
|
}
|
|
321
353
|
queryBuilder.addOptionalQueryTerm(`${propVar} wikibase:rank ${rankVar}.`)
|
|
322
354
|
|
|
@@ -335,7 +367,7 @@ const wikidata = module.exports = {
|
|
|
335
367
|
const readBinding = function(binding)
|
|
336
368
|
{
|
|
337
369
|
const result = {}
|
|
338
|
-
for (const termKey
|
|
370
|
+
for (const termKey of termTimeKeys)
|
|
339
371
|
{
|
|
340
372
|
if (binding[`_${termKey}_ti`])
|
|
341
373
|
{
|
|
@@ -345,6 +377,15 @@ const wikidata = module.exports = {
|
|
|
345
377
|
}
|
|
346
378
|
}
|
|
347
379
|
}
|
|
380
|
+
for (const termKey of termOtherKeys)
|
|
381
|
+
{
|
|
382
|
+
const termOtherVar = `_${termKey}_out`
|
|
383
|
+
if (binding[termOtherVar])
|
|
384
|
+
{
|
|
385
|
+
const valueSplit = binding[termOtherVar].value.split(';')
|
|
386
|
+
result[termKey] = wikidata.extractQidFromUrl(valueSplit[0]) //HACK: does not support multiple values
|
|
387
|
+
}
|
|
388
|
+
}
|
|
348
389
|
return result
|
|
349
390
|
}
|
|
350
391
|
|
|
@@ -353,7 +394,18 @@ const wikidata = module.exports = {
|
|
|
353
394
|
const results = []
|
|
354
395
|
for (const binding of bindings)
|
|
355
396
|
{
|
|
356
|
-
|
|
397
|
+
const newBinding = readBinding(binding)
|
|
398
|
+
const newBindingSerialized = JSON.stringify(newBinding) //HACK: serialization for comparison
|
|
399
|
+
|
|
400
|
+
//HACK: omit perfect duplicates. This can happen with overlapping statements for same position (e.g. Q8423 "position held" for subclass:king)
|
|
401
|
+
var isDupe = false
|
|
402
|
+
for (const result of results)
|
|
403
|
+
{
|
|
404
|
+
isDupe = (JSON.stringify(result) == newBindingSerialized)
|
|
405
|
+
if (isDupe) break
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!isDupe) results.push(readBinding(binding))
|
|
357
409
|
}
|
|
358
410
|
return results
|
|
359
411
|
}
|
|
@@ -428,6 +480,7 @@ const wikidata = module.exports = {
|
|
|
428
480
|
}
|
|
429
481
|
|
|
430
482
|
const queryBuilder = new SparqlBuilder()
|
|
483
|
+
queryBuilder.distinct = true
|
|
431
484
|
queryBuilder.addCacheBuster(this.cacheBuster)
|
|
432
485
|
queryBuilder.addOutParam(itemVar)
|
|
433
486
|
queryBuilder.addOutParam(itemVar + "Label")
|
|
@@ -449,9 +502,10 @@ const wikidata = module.exports = {
|
|
|
449
502
|
newItem.entity = this.extractQidFromUrl(binding[itemVarName].value)
|
|
450
503
|
newItem.generated = true
|
|
451
504
|
const wikidataLabel = binding[itemVarName + "Label"].value
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
505
|
+
|
|
506
|
+
const labelFomat = templateItem.label ? templateItem.label : this.inputSpec.chronicle.defaultLabel
|
|
507
|
+
newItem.label = labelFomat.replaceAll("{_LABEL}", wikidataLabel).replaceAll("{_QID}", newItem.entity)
|
|
508
|
+
|
|
455
509
|
newItems.push(newItem)
|
|
456
510
|
}
|
|
457
511
|
|
package/styles/style.css
CHANGED
|
@@ -1,18 +1,35 @@
|
|
|
1
|
-
.vis-item.vis-range.visc-open-right
|
|
1
|
+
.vis-item.vis-range.visc-open-right, .vis-item.vis-background.visc-open-right
|
|
2
2
|
{
|
|
3
3
|
mask-image: linear-gradient(to right, rgba(0, 0, 0, 1) 50%, rgba(0, 0, 0, 0));
|
|
4
4
|
}
|
|
5
|
-
.vis-item.vis-range.visc-open-left
|
|
5
|
+
.vis-item.vis-range.visc-open-left, .vis-item.vis-background.visc-open-left
|
|
6
6
|
{
|
|
7
7
|
mask-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 50%);
|
|
8
8
|
}
|
|
9
|
-
.vis-item.vis-range.visc-open-
|
|
9
|
+
.vis-item.vis-range.visc-open-left, .vis-item.vis-background.visc-open-left
|
|
10
10
|
{
|
|
11
11
|
mask-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1) 50%, rgba(0, 0, 0, 0));
|
|
12
12
|
}
|
|
13
|
-
.visc-uncertain
|
|
13
|
+
.vis-item.vis-range.visc-uncertain
|
|
14
14
|
{
|
|
15
|
-
background: repeating-linear-gradient(45deg, #
|
|
15
|
+
background: repeating-linear-gradient(45deg, #404040, #404040 8px, rgba(213, 221, 246, 0.4) 8px, rgba(213, 221, 246, 0.4) 16px);
|
|
16
|
+
border-style: dashed;
|
|
17
|
+
}
|
|
18
|
+
.vis-item.vis-background.visc-uncertain
|
|
19
|
+
{
|
|
20
|
+
mask-image: repeating-linear-gradient(45deg, black, black 8px, transparent 8px, transparent 16px);
|
|
21
|
+
border: none;
|
|
22
|
+
}
|
|
23
|
+
.visc-range-overlay
|
|
24
|
+
{
|
|
25
|
+
z-index: 2;
|
|
26
|
+
}
|
|
27
|
+
.vis-item.visc-toplabel
|
|
28
|
+
{
|
|
29
|
+
z-index: 3;
|
|
30
|
+
background: none !important;
|
|
31
|
+
background-color: transparent !important;
|
|
32
|
+
border: 0;
|
|
16
33
|
}
|
|
17
34
|
.vis-item.vis-range.visc-right-connection, .vis-item.vis-range.visc-left-tail
|
|
18
35
|
{
|