vis-chronicle 1.2.4 → 1.3.5
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 +0 -2
- package/package.json +1 -1
- package/src/fetch.js +21 -8
- package/src/index.js +1 -1
- package/src/relativeDates.js +39 -30
- package/src/render.js +6 -1
- package/src/wikidata.js +39 -31
package/README.md
CHANGED
|
@@ -95,8 +95,6 @@ Item properties:
|
|
|
95
95
|
* `general`: General query segment, usually for selecting the item and property.
|
|
96
96
|
* `start`: Query segment for selecting the start value.
|
|
97
97
|
* `end`: Query segment for selecting the end value.
|
|
98
|
-
* `startPath`: Another way of writing Wikidata queries. TODO.
|
|
99
|
-
* `endPath`: See `startPath`
|
|
100
98
|
* `expectedDuration`: Describes the expected duration, for hinting if the start or end is missing.
|
|
101
99
|
* `min`: The absolute minimum duration.
|
|
102
100
|
* `max`: The absolute maximum duration.
|
package/package.json
CHANGED
package/src/fetch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// Uses an input specification file to produce an output file for vis.js Timeline.
|
|
4
4
|
|
|
@@ -112,7 +112,6 @@ entryPoint()
|
|
|
112
112
|
var templateItem = wikidata.inputSpec.items[i]
|
|
113
113
|
if (templateItem.itemQuery || templateItem.items)
|
|
114
114
|
{
|
|
115
|
-
//TODO: caching for item queries
|
|
116
115
|
wikidata.inputSpec.items.splice(i, 1)
|
|
117
116
|
const newItems = await wikidata.createTemplateItems(templateItem)
|
|
118
117
|
for (const newItem of newItems)
|
|
@@ -200,8 +199,16 @@ entryPoint()
|
|
|
200
199
|
{
|
|
201
200
|
if (item.finished) continue
|
|
202
201
|
|
|
203
|
-
if (item.startPath)
|
|
204
|
-
|
|
202
|
+
if (item.startPath)
|
|
203
|
+
{
|
|
204
|
+
item.startPath = wikidata.replaceQueryWildcards(item.startPath, item)
|
|
205
|
+
pathQueries.push(item.startPath)
|
|
206
|
+
}
|
|
207
|
+
if (item.endPath)
|
|
208
|
+
{
|
|
209
|
+
item.endPath = wikidata.replaceQueryWildcards(item.endPath, item)
|
|
210
|
+
pathQueries.push(item.endPath)
|
|
211
|
+
}
|
|
205
212
|
|
|
206
213
|
// the bundle key is the queries, as well as any wildcard parameters
|
|
207
214
|
const keyObject = {}
|
|
@@ -368,8 +375,11 @@ entryPoint()
|
|
|
368
375
|
if (startTime)
|
|
369
376
|
{
|
|
370
377
|
const range = wikidataToRange(startTime)
|
|
371
|
-
|
|
372
|
-
|
|
378
|
+
if (range)
|
|
379
|
+
{
|
|
380
|
+
item.start_min = range.min
|
|
381
|
+
item.start_max = range.max
|
|
382
|
+
}
|
|
373
383
|
}
|
|
374
384
|
else
|
|
375
385
|
console.error(`Date for '${item.startPath}' wasn't cached.`)
|
|
@@ -380,8 +390,11 @@ entryPoint()
|
|
|
380
390
|
if (endTime)
|
|
381
391
|
{
|
|
382
392
|
const range = wikidataToRange(endTime)
|
|
383
|
-
|
|
384
|
-
|
|
393
|
+
if (range)
|
|
394
|
+
{
|
|
395
|
+
item.end_min = range.min
|
|
396
|
+
item.end_max = range.max
|
|
397
|
+
}
|
|
385
398
|
}
|
|
386
399
|
else
|
|
387
400
|
console.error(`Date for '${item.endPath}' wasn't cached.`)
|
package/src/index.js
CHANGED
package/src/relativeDates.js
CHANGED
|
@@ -24,54 +24,61 @@ function durationToWikidataPrecision(duration)
|
|
|
24
24
|
else return 9
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Breaks down a relative date path into its components.
|
|
29
|
+
* @param {*} dateString
|
|
30
|
+
* @returns An array of strings, or null.
|
|
31
|
+
*/
|
|
32
|
+
function breakRelativeDate(dateString)
|
|
29
33
|
{
|
|
30
34
|
if (dateString == '') return null
|
|
31
35
|
|
|
32
36
|
// parse out relative date components
|
|
33
|
-
var relSplit
|
|
34
37
|
var match = dateString.match(wikidata.pathQueryRegex)
|
|
35
|
-
if (match)
|
|
36
|
-
{
|
|
37
|
-
relSplit = match.slice(1)
|
|
38
|
-
}
|
|
39
|
-
else
|
|
38
|
+
if (!match)
|
|
40
39
|
{
|
|
41
40
|
console.error(`Failed to parse relative date '${dateString}'.`)
|
|
42
41
|
return null
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
const dateComponents = [ match[1] ]
|
|
45
|
+
var opStartIndex = 0
|
|
46
|
+
const operatorString = match[2]
|
|
47
|
+
if (operatorString.length > 0)
|
|
48
|
+
{
|
|
49
|
+
for (var i = 1; i < operatorString.length; i++)
|
|
50
|
+
{
|
|
51
|
+
if (operatorString[i] == '+' || operatorString[i] == '>')
|
|
52
|
+
{
|
|
53
|
+
dateComponents.push(operatorString.substring(opStartIndex, i))
|
|
54
|
+
opStartIndex = i
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
dateComponents.push(operatorString.substring(opStartIndex, i))
|
|
58
|
+
}
|
|
59
|
+
return dateComponents
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Flattens a relative date string into a hard date string
|
|
63
|
+
function flattenRelativeDate(wikidataCache, dateString)
|
|
64
|
+
{
|
|
65
|
+
var parsedPath = breakRelativeDate(dateString)
|
|
66
|
+
if (!parsedPath)
|
|
46
67
|
{
|
|
47
68
|
return null
|
|
48
69
|
}
|
|
49
|
-
else if (!wikidataCache[
|
|
70
|
+
else if (!wikidataCache[parsedPath[0]])
|
|
50
71
|
{
|
|
51
|
-
console.error(`Date for '${
|
|
72
|
+
console.error(`Date for '${parsedPath[0]}' wasn't cached.`)
|
|
52
73
|
return null
|
|
53
74
|
}
|
|
54
75
|
else
|
|
55
76
|
{
|
|
56
|
-
const cacheEntry = wikidataCache[
|
|
57
|
-
if (
|
|
77
|
+
const cacheEntry = wikidataCache[parsedPath[0]]
|
|
78
|
+
if (cacheEntry.value && parsedPath.length > 1)
|
|
58
79
|
{
|
|
59
80
|
// break up operators
|
|
60
|
-
const dateOperators =
|
|
61
|
-
var opStartIndex = 0
|
|
62
|
-
const operatorString = relSplit[1]
|
|
63
|
-
if (operatorString.length > 0)
|
|
64
|
-
{
|
|
65
|
-
for (var i = 1; i < operatorString.length; i++)
|
|
66
|
-
{
|
|
67
|
-
if (operatorString[i] == '+' || operatorString[i] == '>')
|
|
68
|
-
{
|
|
69
|
-
dateOperators.push(operatorString.substring(opStartIndex, i))
|
|
70
|
-
opStartIndex = i
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
dateOperators.push(operatorString.substring(opStartIndex, i))
|
|
74
|
-
}
|
|
81
|
+
const dateOperators = parsedPath.slice(1)
|
|
75
82
|
|
|
76
83
|
// handle relative segments of date
|
|
77
84
|
// About precision:
|
|
@@ -136,7 +143,7 @@ module.exports = function flattenRelativeDate(wikidataCache, dateString)
|
|
|
136
143
|
}
|
|
137
144
|
else
|
|
138
145
|
{
|
|
139
|
-
console.error(`Cannot advance/'>' to '${
|
|
146
|
+
console.error(`Cannot advance/'>' to '${component.substring(1)}'.`)
|
|
140
147
|
}
|
|
141
148
|
}
|
|
142
149
|
else
|
|
@@ -156,4 +163,6 @@ module.exports = function flattenRelativeDate(wikidataCache, dateString)
|
|
|
156
163
|
return {...cacheEntry}
|
|
157
164
|
}
|
|
158
165
|
}
|
|
159
|
-
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = { breakRelativeDate, flattenRelativeDate }
|
package/src/render.js
CHANGED
|
@@ -461,7 +461,12 @@ renderer.produceOutput = function(inputSpec, items)
|
|
|
461
461
|
{
|
|
462
462
|
const labelItem = {...outputItem}
|
|
463
463
|
labelItem.id += "-label"
|
|
464
|
-
|
|
464
|
+
|
|
465
|
+
var classes = labelItem.className.split(' ')
|
|
466
|
+
classes = classes.filter(c => c != "visc-left-connection" && c != "visc-right-connection")
|
|
467
|
+
classes.push("visc-toplabel")
|
|
468
|
+
labelItem.className = classes.join(' ')
|
|
469
|
+
|
|
465
470
|
outputObject.items.push(labelItem)
|
|
466
471
|
outputItem.content = outputItem.content ? " " : ""
|
|
467
472
|
}
|
package/src/wikidata.js
CHANGED
|
@@ -12,9 +12,9 @@ const wikidata = module.exports = {
|
|
|
12
12
|
verboseLogging: false,
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Caches results for SPARQL queries, using the query string as the key.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
queryCache: {},
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Caches results for path queries, using the path as the key.
|
|
@@ -25,9 +25,9 @@ const wikidata = module.exports = {
|
|
|
25
25
|
cacheBuster: undefined,
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Relative path to the
|
|
28
|
+
* Relative path to the query cache file.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
queryCacheFile: "intermediate/sparql-query-cache.json",
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Relative path to the path cache file.
|
|
@@ -126,8 +126,8 @@ const wikidata = module.exports = {
|
|
|
126
126
|
{
|
|
127
127
|
try
|
|
128
128
|
{
|
|
129
|
-
const contents = await fs.promises.readFile(this.
|
|
130
|
-
this.
|
|
129
|
+
const contents = await fs.promises.readFile(this.queryCacheFile)
|
|
130
|
+
this.queryCache = JSON.parse(contents)
|
|
131
131
|
}
|
|
132
132
|
catch
|
|
133
133
|
{
|
|
@@ -147,11 +147,11 @@ const wikidata = module.exports = {
|
|
|
147
147
|
|
|
148
148
|
writeCache: async function()
|
|
149
149
|
{
|
|
150
|
-
await mypath.ensureDirectoryForFile(this.
|
|
150
|
+
await mypath.ensureDirectoryForFile(this.queryCacheFile)
|
|
151
151
|
|
|
152
|
-
fs.writeFile(this.
|
|
152
|
+
fs.writeFile(this.queryCacheFile, JSON.stringify(this.queryCache), err => {
|
|
153
153
|
if (err) {
|
|
154
|
-
console.error(`Error writing wikidata
|
|
154
|
+
console.error(`Error writing wikidata query cache:`)
|
|
155
155
|
console.error(err)
|
|
156
156
|
}
|
|
157
157
|
})
|
|
@@ -184,25 +184,32 @@ const wikidata = module.exports = {
|
|
|
184
184
|
}
|
|
185
185
|
},
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
replaceQueryWildcards: function(term, item, itemPrefix = "")
|
|
188
188
|
{
|
|
189
|
-
if (!term)
|
|
190
|
-
{
|
|
191
|
-
return term;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
189
|
// replace query wildcards
|
|
195
190
|
for (const key in item)
|
|
196
191
|
{
|
|
197
192
|
var insertValue = item[key]
|
|
198
193
|
if (typeof insertValue === "string" && insertValue.startsWith("Q"))
|
|
199
|
-
insertValue =
|
|
194
|
+
insertValue = itemPrefix + insertValue
|
|
200
195
|
term = term.replaceAll(`{${key}}`, insertValue)
|
|
201
196
|
}
|
|
202
197
|
|
|
203
198
|
// detect unreplaced wildcards
|
|
204
199
|
//TODO:
|
|
205
200
|
|
|
201
|
+
return term
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
preprocessQueryTerm: function(context, term, item)
|
|
205
|
+
{
|
|
206
|
+
if (!term)
|
|
207
|
+
{
|
|
208
|
+
return term
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
term = this.replaceQueryWildcards(term, item, "wd:")
|
|
212
|
+
|
|
206
213
|
// terminate term
|
|
207
214
|
if (!term.trim().endsWith("."))
|
|
208
215
|
{
|
|
@@ -238,7 +245,7 @@ const wikidata = module.exports = {
|
|
|
238
245
|
|
|
239
246
|
//TODO: validate query has required wildcards
|
|
240
247
|
|
|
241
|
-
queryTerm = this.
|
|
248
|
+
queryTerm = this.preprocessQueryTerm(inQueryTerm, queryTerm, item)
|
|
242
249
|
return queryTerm
|
|
243
250
|
},
|
|
244
251
|
|
|
@@ -275,7 +282,7 @@ const wikidata = module.exports = {
|
|
|
275
282
|
if (typeof queryTerm === 'string' || queryTerm instanceof String)
|
|
276
283
|
{
|
|
277
284
|
return {
|
|
278
|
-
value: this.
|
|
285
|
+
value: this.preprocessQueryTerm(inQueryTerm, queryTerm, item),
|
|
279
286
|
min: "?_prop pqv:P1319 ?_min_value.",
|
|
280
287
|
max: "?_prop pqv:P1326 ?_max_value."
|
|
281
288
|
}
|
|
@@ -285,7 +292,7 @@ const wikidata = module.exports = {
|
|
|
285
292
|
const result = {}
|
|
286
293
|
for (const key in queryTerm)
|
|
287
294
|
{
|
|
288
|
-
result[key] = this.
|
|
295
|
+
result[key] = this.preprocessQueryTerm(inQueryTerm, queryTerm[key], item)
|
|
289
296
|
}
|
|
290
297
|
return result
|
|
291
298
|
}
|
|
@@ -393,15 +400,7 @@ const wikidata = module.exports = {
|
|
|
393
400
|
queryBuilder.addOptionalQueryTerm(`${propVar} wikibase:rank ${rankVar}.`)
|
|
394
401
|
|
|
395
402
|
const query = queryBuilder.build()
|
|
396
|
-
|
|
397
|
-
// read cache
|
|
398
|
-
const cacheKey = query
|
|
399
|
-
if (!this.skipCache && !item.skipCache && this.cache[cacheKey])
|
|
400
|
-
{
|
|
401
|
-
return this.cache[cacheKey]
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const data = await this.runQuery(query)
|
|
403
|
+
const data = await this.runQuery(query, item.skipCache)
|
|
405
404
|
console.log(`\tQuery for ${item.id} returned ${data.results.bindings.length} results.`)
|
|
406
405
|
|
|
407
406
|
const readBinding = function(binding)
|
|
@@ -487,7 +486,6 @@ const wikidata = module.exports = {
|
|
|
487
486
|
}
|
|
488
487
|
}
|
|
489
488
|
|
|
490
|
-
this.cache[cacheKey] = result;
|
|
491
489
|
return result;
|
|
492
490
|
},
|
|
493
491
|
|
|
@@ -695,7 +693,11 @@ const wikidata = module.exports = {
|
|
|
695
693
|
queryBuilder.addWikibaseLabel(this.lang)
|
|
696
694
|
|
|
697
695
|
const query = queryBuilder.build()
|
|
698
|
-
const data = await this.runQuery(query)
|
|
696
|
+
const data = await this.runQuery(query, templateItem.skipCache)
|
|
697
|
+
if (!data)
|
|
698
|
+
{
|
|
699
|
+
throw 'No response data from Wikidata query.'
|
|
700
|
+
}
|
|
699
701
|
|
|
700
702
|
const newItems = []
|
|
701
703
|
|
|
@@ -723,8 +725,13 @@ const wikidata = module.exports = {
|
|
|
723
725
|
},
|
|
724
726
|
|
|
725
727
|
// runs a SPARQL query
|
|
726
|
-
runQuery: async function(query)
|
|
728
|
+
runQuery: async function(query, skipCache = false)
|
|
727
729
|
{
|
|
730
|
+
if (!skipCache && !this.skipCache && this.queryCache[query])
|
|
731
|
+
{
|
|
732
|
+
return this.queryCache[query]
|
|
733
|
+
}
|
|
734
|
+
|
|
728
735
|
if (this.verboseLogging) console.log(query)
|
|
729
736
|
|
|
730
737
|
assert(this.options)
|
|
@@ -739,6 +746,7 @@ const wikidata = module.exports = {
|
|
|
739
746
|
else
|
|
740
747
|
{
|
|
741
748
|
const data = await response.json()
|
|
749
|
+
this.queryCache[query] = data
|
|
742
750
|
return data
|
|
743
751
|
}
|
|
744
752
|
}
|