@vizabi/reader-ddfcsv 4.1.1 → 4.3.0
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/CHANGELOG.md +5 -0
- package/dist/reader-ddfcsv-polyfill.js +1 -16
- package/dist/reader-ddfcsv-polyfill.js.map +1 -1
- package/dist/reader-ddfcsv.js +7 -16
- package/dist/reader-ddfcsv.js.map +1 -1
- package/lib/src/ddf-csv.d.ts +1 -0
- package/lib/src/ddf-csv.js +31 -20
- package/lib/src/ddf-csv.js.map +1 -1
- package/lib/src/ddfcsv-error.js +1 -0
- package/lib/src/ddfcsv-error.js.map +1 -1
- package/lib/src/ddfcsv-reader.js +6 -5
- package/lib/src/ddfcsv-reader.js.map +1 -1
- package/lib/src/file-readers/backend-file-reader.js +1 -0
- package/lib/src/file-readers/backend-file-reader.js.map +1 -1
- package/lib/src/file-readers/github-path-adapter.js +1 -0
- package/lib/src/file-readers/github-path-adapter.js.map +1 -1
- package/lib/src/index.js +5 -4
- package/lib/src/index.js.map +1 -1
- package/lib/src/interfaces.d.ts +1 -0
- package/lib/src/resource-selection-optimizer/in-clause-under-conjunction.d.ts +3 -2
- package/lib/src/resource-selection-optimizer/in-clause-under-conjunction.js +35 -35
- package/lib/src/resource-selection-optimizer/in-clause-under-conjunction.js.map +1 -1
- package/lib/src/resource-selection-optimizer/index.d.ts +1 -1
- package/lib/src/resource-selection-optimizer/index.js +3 -2
- package/lib/src/resource-selection-optimizer/index.js.map +1 -1
- package/lib-web/src/ddf-csv.d.ts +1 -0
- package/lib-web/src/ddf-csv.js +285 -388
- package/lib-web/src/ddf-csv.js.map +1 -1
- package/lib-web/src/ddfcsv-error.js +9 -12
- package/lib-web/src/ddfcsv-error.js.map +1 -1
- package/lib-web/src/ddfcsv-reader.js +84 -125
- package/lib-web/src/ddfcsv-reader.js.map +1 -1
- package/lib-web/src/file-readers/frontend-file-reader.js +14 -16
- package/lib-web/src/file-readers/frontend-file-reader.js.map +1 -1
- package/lib-web/src/file-readers/github-path-adapter.js +6 -5
- package/lib-web/src/file-readers/github-path-adapter.js.map +1 -1
- package/lib-web/src/index-web.js +6 -5
- package/lib-web/src/index-web.js.map +1 -1
- package/lib-web/src/interfaces.d.ts +1 -0
- package/lib-web/src/resource-selection-optimizer/in-clause-under-conjunction.d.ts +3 -2
- package/lib-web/src/resource-selection-optimizer/in-clause-under-conjunction.js +168 -347
- package/lib-web/src/resource-selection-optimizer/in-clause-under-conjunction.js.map +1 -1
- package/lib-web/src/resource-selection-optimizer/index.d.ts +1 -1
- package/lib-web/src/resource-selection-optimizer/index.js +7 -6
- package/lib-web/src/resource-selection-optimizer/index.js.map +1 -1
- package/package.json +6 -4
- package/src/ddf-csv.ts +18 -12
- package/src/interfaces.ts +1 -0
- package/src/resource-selection-optimizer/in-clause-under-conjunction.ts +37 -38
- package/src/resource-selection-optimizer/index.ts +2 -2
- package/tsconfig-web.json +5 -2
- package/tsconfig.json +1 -1
package/lib-web/src/ddf-csv.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var isValidNumeric = function (val) { return typeof val !== 'number' && !val ? false : true; };
|
|
3
|
+
exports.ddfCsvReader = void 0;
|
|
4
|
+
const includes = require("lodash.includes");
|
|
5
|
+
const isEmpty = require("lodash.isempty");
|
|
6
|
+
const stripBom = require("strip-bom");
|
|
7
|
+
const resource_selection_optimizer_1 = require("./resource-selection-optimizer");
|
|
8
|
+
const ddfcsv_error_1 = require("./ddfcsv-error");
|
|
9
|
+
const ddf_query_validator_1 = require("ddf-query-validator");
|
|
10
|
+
const Papa = require("papaparse");
|
|
11
|
+
const d3_time_format_1 = require("d3-time-format");
|
|
12
|
+
const isValidNumeric = val => typeof val !== 'number' && !val ? false : true;
|
|
14
13
|
function ddfCsvReader(logger) {
|
|
15
|
-
|
|
14
|
+
const internalConcepts = [
|
|
16
15
|
{ concept: 'concept', concept_type: 'string', domain: null },
|
|
17
16
|
{ concept: 'concept_type', concept_type: 'string', domain: null }
|
|
18
17
|
];
|
|
19
|
-
|
|
20
|
-
['$and',
|
|
21
|
-
['$or',
|
|
22
|
-
['$not',
|
|
23
|
-
['$nor',
|
|
24
|
-
['$eq',
|
|
25
|
-
['$ne',
|
|
26
|
-
['$gt',
|
|
27
|
-
['$gte',
|
|
28
|
-
['$lt',
|
|
29
|
-
['$lte',
|
|
30
|
-
['$in',
|
|
31
|
-
['$nin',
|
|
18
|
+
const operators = new Map([
|
|
19
|
+
['$and', (row, predicates) => predicates.every(p => applyFilterRow(row, p))],
|
|
20
|
+
['$or', (row, predicates) => predicates.some(p => applyFilterRow(row, p))],
|
|
21
|
+
['$not', (row, predicate) => !applyFilterRow(row, predicate)],
|
|
22
|
+
['$nor', (row, predicates) => !predicates.some(p => applyFilterRow(row, p))],
|
|
23
|
+
['$eq', (rowValue, filterValue) => rowValue == filterValue],
|
|
24
|
+
['$ne', (rowValue, filterValue) => rowValue != filterValue],
|
|
25
|
+
['$gt', (rowValue, filterValue) => isValidNumeric(rowValue) && rowValue > filterValue],
|
|
26
|
+
['$gte', (rowValue, filterValue) => isValidNumeric(rowValue) && rowValue >= filterValue],
|
|
27
|
+
['$lt', (rowValue, filterValue) => isValidNumeric(rowValue) && rowValue < filterValue],
|
|
28
|
+
['$lte', (rowValue, filterValue) => isValidNumeric(rowValue) && rowValue <= filterValue],
|
|
29
|
+
['$in', (rowValue, filterValue) => filterValue.has(rowValue)],
|
|
30
|
+
['$nin', (rowValue, filterValue) => !filterValue.has(rowValue)],
|
|
32
31
|
]);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const keyValueLookup = new Map();
|
|
33
|
+
const resourcesLookup = new Map();
|
|
34
|
+
let optimalFilesSet = [];
|
|
35
|
+
let datapackage;
|
|
36
|
+
let datapackagePromise;
|
|
37
37
|
function loadDataPackage(baseOptions) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return new Promise(
|
|
38
|
+
const datapackagePath = (0, ddf_query_validator_1.getFilePath)(baseOptions.basePath);
|
|
39
|
+
const { debug, error } = baseOptions.diagnostic.prepareDiagnosticFor('loadDataPackage');
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
41
|
if (datapackage) {
|
|
42
42
|
return resolve(datapackage);
|
|
43
43
|
}
|
|
44
|
-
baseOptions.fileReader.readText(datapackagePath,
|
|
44
|
+
baseOptions.fileReader.readText(datapackagePath, (err, data) => {
|
|
45
45
|
if (err) {
|
|
46
46
|
error('file reading', err);
|
|
47
47
|
return reject(new ddfcsv_error_1.DdfCsvError(ddfcsv_error_1.FILE_READING_ERROR, err, datapackagePath));
|
|
@@ -61,83 +61,58 @@ function ddfCsvReader(logger) {
|
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
-
function loadConcepts(queryParam, options) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
buildConceptsLookup(concepts, options);
|
|
83
|
-
return [4, reparseConcepts(options)];
|
|
84
|
-
case 3:
|
|
85
|
-
result = _a.sent();
|
|
86
|
-
return [3, 5];
|
|
87
|
-
case 4:
|
|
88
|
-
err_1 = _a.sent();
|
|
89
|
-
error('concepts processing', err_1);
|
|
90
|
-
throw err_1;
|
|
91
|
-
case 5: return [2, result];
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
});
|
|
64
|
+
async function loadConcepts(queryParam, options) {
|
|
65
|
+
const { error } = options.diagnostic.prepareDiagnosticFor('loadConcepts');
|
|
66
|
+
setConceptsLookup(internalConcepts, options);
|
|
67
|
+
const conceptQuery = {
|
|
68
|
+
select: { key: ['concept'], value: ['concept_type', 'domain'] },
|
|
69
|
+
from: 'concepts'
|
|
70
|
+
};
|
|
71
|
+
let result;
|
|
72
|
+
try {
|
|
73
|
+
const concepts = await queryData(conceptQuery, options);
|
|
74
|
+
buildConceptsLookup(concepts, options);
|
|
75
|
+
result = await reparseConcepts(options);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
error('concepts processing', err);
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
95
82
|
}
|
|
96
83
|
function buildConceptsLookup(concepts, options) {
|
|
97
|
-
|
|
98
|
-
.filter(
|
|
99
|
-
.map(
|
|
84
|
+
const entitySetMembershipConcepts = concepts
|
|
85
|
+
.filter(concept => concept.concept_type === 'entity_set')
|
|
86
|
+
.map(concept => ({
|
|
100
87
|
concept: 'is--' + concept.concept,
|
|
101
88
|
concept_type: 'boolean',
|
|
102
89
|
domain: null
|
|
103
|
-
})
|
|
90
|
+
}));
|
|
104
91
|
concepts = concepts
|
|
105
92
|
.concat(entitySetMembershipConcepts)
|
|
106
93
|
.concat(internalConcepts);
|
|
107
94
|
setConceptsLookup(concepts, options);
|
|
108
95
|
}
|
|
109
|
-
function reparseConcepts(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
['
|
|
113
|
-
['measure', function (str) { return parseFloat(str); }]
|
|
96
|
+
function reparseConcepts({ conceptsLookup }) {
|
|
97
|
+
const parsingFunctions = new Map([
|
|
98
|
+
['boolean', (str) => str === 'true' || str === 'TRUE'],
|
|
99
|
+
['measure', (str) => parseFloat(str)]
|
|
114
100
|
]);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return resource.data.then(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
resourceConcepts.forEach(
|
|
121
|
-
|
|
122
|
-
|
|
101
|
+
const resources = getResources(['concept']);
|
|
102
|
+
const resourceUpdates = [...resources].map(resource => {
|
|
103
|
+
return resource.data.then(response => {
|
|
104
|
+
const resourceConcepts = Object.keys(response.data[0]);
|
|
105
|
+
const parsingConcepts = new Map();
|
|
106
|
+
resourceConcepts.forEach(concept => {
|
|
107
|
+
const type = conceptsLookup.get(concept).concept_type;
|
|
108
|
+
const fn = parsingFunctions.get(type);
|
|
123
109
|
if (fn) {
|
|
124
110
|
parsingConcepts.set(concept, fn);
|
|
125
111
|
}
|
|
126
112
|
});
|
|
127
|
-
return response.data.forEach(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
for (var parsingConcepts_1 = tslib_1.__values(parsingConcepts), parsingConcepts_1_1 = parsingConcepts_1.next(); !parsingConcepts_1_1.done; parsingConcepts_1_1 = parsingConcepts_1.next()) {
|
|
131
|
-
var _b = tslib_1.__read(parsingConcepts_1_1.value, 2), concept = _b[0], parseFn = _b[1];
|
|
132
|
-
row[concept] = parseFn(row[concept]);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
136
|
-
finally {
|
|
137
|
-
try {
|
|
138
|
-
if (parsingConcepts_1_1 && !parsingConcepts_1_1.done && (_a = parsingConcepts_1.return)) _a.call(parsingConcepts_1);
|
|
139
|
-
}
|
|
140
|
-
finally { if (e_1) throw e_1.error; }
|
|
113
|
+
return response.data.forEach(row => {
|
|
114
|
+
for (const [concept, parseFn] of parsingConcepts) {
|
|
115
|
+
row[concept] = parseFn(row[concept]);
|
|
141
116
|
}
|
|
142
117
|
});
|
|
143
118
|
});
|
|
@@ -146,84 +121,61 @@ function ddfCsvReader(logger) {
|
|
|
146
121
|
}
|
|
147
122
|
function setConceptsLookup(concepts, options) {
|
|
148
123
|
options.conceptsLookup.clear();
|
|
149
|
-
concepts.forEach(
|
|
150
|
-
}
|
|
151
|
-
function query(queryParam, baseOptions) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return [4, ddf_query_validator_1.validateQueryDefinitions(queryParam, baseOptions)];
|
|
172
|
-
case 5:
|
|
173
|
-
_c.sent();
|
|
174
|
-
if (!ddf_query_validator_1.isSchemaQuery(queryParam)) return [3, 7];
|
|
175
|
-
return [4, querySchema(queryParam, baseOptions)];
|
|
176
|
-
case 6:
|
|
177
|
-
data = _c.sent();
|
|
178
|
-
return [3, 11];
|
|
179
|
-
case 7:
|
|
180
|
-
appropriatePlugin = resource_selection_optimizer_1.getAppropriatePlugin(queryParam, baseOptions);
|
|
181
|
-
if (!appropriatePlugin) return [3, 9];
|
|
182
|
-
optimalFilesSet = [];
|
|
183
|
-
return [4, appropriatePlugin.getRecommendedFilesSet()];
|
|
184
|
-
case 8:
|
|
185
|
-
files = _c.sent();
|
|
186
|
-
optimalFilesSet = files;
|
|
187
|
-
queryParam.optimalFilesSet = [].concat(files, queryParam.optimalFilesSet);
|
|
188
|
-
warning('get custom optimal files list by a plugin', optimalFilesSet);
|
|
189
|
-
_c.label = 9;
|
|
190
|
-
case 9: return [4, queryData(queryParam, baseOptions)];
|
|
191
|
-
case 10:
|
|
192
|
-
data = _c.sent();
|
|
193
|
-
_c.label = 11;
|
|
194
|
-
case 11: return [3, 13];
|
|
195
|
-
case 12:
|
|
196
|
-
err_2 = _c.sent();
|
|
197
|
-
error('general query error', err_2);
|
|
198
|
-
throw err_2;
|
|
199
|
-
case 13: return [2, data];
|
|
124
|
+
concepts.forEach(row => options.conceptsLookup.set(row.concept, row));
|
|
125
|
+
}
|
|
126
|
+
async function query(queryParam, baseOptions) {
|
|
127
|
+
const { warning, error } = baseOptions.diagnostic.prepareDiagnosticFor('query');
|
|
128
|
+
let data;
|
|
129
|
+
try {
|
|
130
|
+
await (0, ddf_query_validator_1.validateQueryStructure)(queryParam, baseOptions);
|
|
131
|
+
baseOptions.datapackage = await (datapackagePromise || (datapackagePromise = loadDataPackage(baseOptions)));
|
|
132
|
+
baseOptions.resourcesLookup = resourcesLookup;
|
|
133
|
+
await loadConcepts(queryParam, baseOptions);
|
|
134
|
+
await (0, ddf_query_validator_1.validateQueryDefinitions)(queryParam, baseOptions);
|
|
135
|
+
if ((0, ddf_query_validator_1.isSchemaQuery)(queryParam)) {
|
|
136
|
+
data = await querySchema(queryParam, baseOptions);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const appropriatePlugin = (0, resource_selection_optimizer_1.getAppropriatePlugin)(this, queryParam, baseOptions);
|
|
140
|
+
if (appropriatePlugin) {
|
|
141
|
+
optimalFilesSet = [];
|
|
142
|
+
const files = await appropriatePlugin.getRecommendedFilesSet();
|
|
143
|
+
optimalFilesSet = files;
|
|
144
|
+
queryParam.optimalFilesSet = [].concat(files, queryParam.optimalFilesSet);
|
|
145
|
+
warning('get custom optimal files list by a plugin', optimalFilesSet);
|
|
200
146
|
}
|
|
201
|
-
|
|
202
|
-
|
|
147
|
+
data = await queryData(queryParam, baseOptions);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
error('general query error', err);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
return data;
|
|
203
155
|
}
|
|
204
156
|
function queryData(queryParam, options) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
157
|
+
const { debug } = options.diagnostic.prepareDiagnosticFor('queryData');
|
|
158
|
+
const { select: { key = [], value = [] }, from = '', where = {}, join = {}, order_by = [], language } = queryParam;
|
|
159
|
+
const select = { key, value };
|
|
208
160
|
debug('start all data loading', queryParam);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
161
|
+
const projection = new Set(select.key.concat(select.value));
|
|
162
|
+
const filterFields = getFilterFields(where).filter(field => from === 'entities' || !projection.has(field));
|
|
163
|
+
const resourcesPromise = loadResources(select.key, [...select.value, ...filterFields], language, options, queryParam);
|
|
164
|
+
const joinsPromise = getJoinFilters(join, queryParam, options);
|
|
165
|
+
const entitySetFilterPromise = getEntitySetFilter(select.key, queryParam, options);
|
|
214
166
|
return Promise.all([resourcesPromise, entitySetFilterPromise, joinsPromise])
|
|
215
|
-
.then(
|
|
216
|
-
var _b = tslib_1.__read(_a, 3), resourceResponses = _b[0], entitySetFilter = _b[1], joinFilters = _b[2];
|
|
167
|
+
.then(([resourceResponses, entitySetFilter, joinFilters]) => {
|
|
217
168
|
debug('finish all data loading', queryParam);
|
|
218
|
-
|
|
219
|
-
|
|
169
|
+
const whereResolved = processWhere(where, joinFilters);
|
|
170
|
+
const filter = mergeFilters(entitySetFilter, whereResolved);
|
|
220
171
|
debug('dataTables processing', queryParam);
|
|
221
|
-
|
|
222
|
-
.map(
|
|
172
|
+
const dataTables = resourceResponses
|
|
173
|
+
.map(response => processResourceResponse(response, select, filterFields, options));
|
|
223
174
|
debug('queryResult processing', queryParam);
|
|
224
|
-
|
|
225
|
-
.
|
|
226
|
-
.map(
|
|
175
|
+
const queryResult = joinData(select.key, 'overwrite', ...dataTables)
|
|
176
|
+
.filter(row => applyFilterRow(row, filter))
|
|
177
|
+
.map(row => fillMissingValues(row, projection))
|
|
178
|
+
.map(row => projectRow(row, projection));
|
|
227
179
|
debug('result ordering', queryParam);
|
|
228
180
|
orderData(queryResult, order_by);
|
|
229
181
|
debug('final result is ready', queryParam);
|
|
@@ -231,33 +183,32 @@ function ddfCsvReader(logger) {
|
|
|
231
183
|
});
|
|
232
184
|
}
|
|
233
185
|
function parseTime(result, options) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
timeConcepts.forEach(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
result.forEach(function (row) {
|
|
186
|
+
const conceptsLookup = options.conceptsLookup;
|
|
187
|
+
const concepts = Object.keys(result[0] || {});
|
|
188
|
+
const timeConcepts = concepts.map(c => conceptsLookup.get(c) || {}).filter(co => co.concept_type == 'time');
|
|
189
|
+
timeConcepts.forEach(({ concept }) => {
|
|
190
|
+
const parse = getTimeParser(concept, options);
|
|
191
|
+
result.forEach(row => {
|
|
241
192
|
row[concept] = parse(row[concept]);
|
|
242
193
|
});
|
|
243
194
|
});
|
|
244
195
|
return result;
|
|
245
196
|
}
|
|
246
197
|
function getTimeParser(concept, options) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
year: d3_time_format_1.utcParse('%Y'),
|
|
250
|
-
month: d3_time_format_1.utcParse('%Y-%m'),
|
|
251
|
-
day: d3_time_format_1.utcParse('%Y%m%d'),
|
|
252
|
-
hour: d3_time_format_1.utcParse('%Y%m%dt%H'),
|
|
253
|
-
minute: d3_time_format_1.utcParse('%Y%m%dt%H%M'),
|
|
254
|
-
second: d3_time_format_1.utcParse('%Y%m%dt%H%M%S'),
|
|
255
|
-
week: d3_time_format_1.utcParse('%Yw%V'),
|
|
256
|
-
quarter: d3_time_format_1.utcParse('%Yq%q')
|
|
198
|
+
const { error } = options.diagnostic.prepareDiagnosticFor('queryData');
|
|
199
|
+
const parsers = {
|
|
200
|
+
year: (0, d3_time_format_1.utcParse)('%Y'),
|
|
201
|
+
month: (0, d3_time_format_1.utcParse)('%Y-%m'),
|
|
202
|
+
day: (0, d3_time_format_1.utcParse)('%Y%m%d'),
|
|
203
|
+
hour: (0, d3_time_format_1.utcParse)('%Y%m%dt%H'),
|
|
204
|
+
minute: (0, d3_time_format_1.utcParse)('%Y%m%dt%H%M'),
|
|
205
|
+
second: (0, d3_time_format_1.utcParse)('%Y%m%dt%H%M%S'),
|
|
206
|
+
week: (0, d3_time_format_1.utcParse)('%Yw%V'),
|
|
207
|
+
quarter: (0, d3_time_format_1.utcParse)('%Yq%q')
|
|
257
208
|
};
|
|
258
209
|
function tryParse(str) {
|
|
259
|
-
for (
|
|
260
|
-
|
|
210
|
+
for (const i in parsers) {
|
|
211
|
+
const dateObject = parsers[i](str);
|
|
261
212
|
if (dateObject) {
|
|
262
213
|
return dateObject;
|
|
263
214
|
}
|
|
@@ -270,29 +221,28 @@ function ddfCsvReader(logger) {
|
|
|
270
221
|
}
|
|
271
222
|
if (!parsers[concept]) {
|
|
272
223
|
error('No time parser found for time concept: ' + concept);
|
|
273
|
-
return
|
|
224
|
+
return str => str;
|
|
274
225
|
}
|
|
275
226
|
return parsers[concept];
|
|
276
227
|
}
|
|
277
|
-
function orderData(data, orderBy) {
|
|
278
|
-
if (orderBy === void 0) { orderBy = []; }
|
|
228
|
+
function orderData(data, orderBy = []) {
|
|
279
229
|
if (orderBy.length === 0) {
|
|
280
230
|
return;
|
|
281
231
|
}
|
|
282
|
-
|
|
232
|
+
const orderNormalized = orderBy.map(orderPart => {
|
|
283
233
|
if (typeof orderPart === 'string') {
|
|
284
234
|
return { concept: orderPart, direction: 1 };
|
|
285
235
|
}
|
|
286
236
|
else {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return { concept
|
|
237
|
+
const concept = Object.keys(orderPart)[0];
|
|
238
|
+
const direction = (orderPart[concept] === 'asc' ? 1 : -1);
|
|
239
|
+
return { concept, direction };
|
|
290
240
|
}
|
|
291
241
|
});
|
|
292
|
-
|
|
293
|
-
data.sort(
|
|
294
|
-
for (
|
|
295
|
-
|
|
242
|
+
const n = orderNormalized.length;
|
|
243
|
+
data.sort((a, b) => {
|
|
244
|
+
for (let i = 0; i < n; i++) {
|
|
245
|
+
const order = orderNormalized[i];
|
|
296
246
|
if (a[order.concept] < b[order.concept]) {
|
|
297
247
|
return -1 * order.direction;
|
|
298
248
|
}
|
|
@@ -304,11 +254,11 @@ function ddfCsvReader(logger) {
|
|
|
304
254
|
});
|
|
305
255
|
}
|
|
306
256
|
function processWhere(where, joinFilters) {
|
|
307
|
-
|
|
308
|
-
for (
|
|
309
|
-
|
|
257
|
+
const result = {};
|
|
258
|
+
for (const field in where) {
|
|
259
|
+
const fieldValue = where[field];
|
|
310
260
|
if (includes(['$and', '$or', '$nor'], field)) {
|
|
311
|
-
result[field] = fieldValue.map(
|
|
261
|
+
result[field] = fieldValue.map(subFilter => processWhere(subFilter, joinFilters));
|
|
312
262
|
}
|
|
313
263
|
else if (field === '$in' || field === '$nin') {
|
|
314
264
|
result[field] = new Set(fieldValue);
|
|
@@ -325,62 +275,44 @@ function ddfCsvReader(logger) {
|
|
|
325
275
|
}
|
|
326
276
|
return result;
|
|
327
277
|
}
|
|
328
|
-
function mergeFilters() {
|
|
329
|
-
|
|
330
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
331
|
-
filters[_i] = arguments[_i];
|
|
332
|
-
}
|
|
333
|
-
return filters.reduce(function (a, b) {
|
|
278
|
+
function mergeFilters(...filters) {
|
|
279
|
+
return filters.reduce((a, b) => {
|
|
334
280
|
a.$and.push(b);
|
|
335
281
|
return a;
|
|
336
282
|
}, { $and: [] });
|
|
337
283
|
}
|
|
338
284
|
function querySchema(queryParam, baseOptions) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
debug(
|
|
342
|
-
return baseOptions.datapackage.ddfSchema[collectionPar].map(
|
|
343
|
-
var primaryKey = _a.primaryKey, value = _a.value;
|
|
344
|
-
return ({ key: primaryKey, value: value });
|
|
345
|
-
});
|
|
285
|
+
const { debug, error } = baseOptions.diagnostic.prepareDiagnosticFor('query');
|
|
286
|
+
const getSchemaFromCollection = collectionPar => {
|
|
287
|
+
debug(`get schema for collection ${collectionPar}`);
|
|
288
|
+
return baseOptions.datapackage.ddfSchema[collectionPar].map(({ primaryKey, value }) => ({ key: primaryKey, value }));
|
|
346
289
|
};
|
|
347
|
-
|
|
290
|
+
const collection = queryParam.from.split('.')[0];
|
|
348
291
|
if (baseOptions.datapackage.ddfSchema[collection]) {
|
|
349
292
|
return getSchemaFromCollection(collection);
|
|
350
293
|
}
|
|
351
294
|
else if (collection === '*') {
|
|
352
295
|
return Object.keys(baseOptions.datapackage.ddfSchema)
|
|
353
296
|
.map(getSchemaFromCollection)
|
|
354
|
-
.reduce(
|
|
297
|
+
.reduce((a, b) => a.concat(b));
|
|
355
298
|
}
|
|
356
299
|
else {
|
|
357
|
-
|
|
300
|
+
const message = `No valid collection (${collection}) for schema query`;
|
|
358
301
|
error(message);
|
|
359
302
|
throwError(new ddfcsv_error_1.DdfCsvError(ddfcsv_error_1.DDF_ERROR, message));
|
|
360
303
|
}
|
|
361
304
|
}
|
|
362
305
|
function fillMissingValues(row, projection) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
var field = projection_1_1.value;
|
|
367
|
-
if (typeof row[field] === 'undefined') {
|
|
368
|
-
row[field] = null;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
373
|
-
finally {
|
|
374
|
-
try {
|
|
375
|
-
if (projection_1_1 && !projection_1_1.done && (_a = projection_1.return)) _a.call(projection_1);
|
|
306
|
+
for (const field of projection) {
|
|
307
|
+
if (typeof row[field] === 'undefined') {
|
|
308
|
+
row[field] = null;
|
|
376
309
|
}
|
|
377
|
-
finally { if (e_2) throw e_2.error; }
|
|
378
310
|
}
|
|
379
311
|
return row;
|
|
380
312
|
}
|
|
381
313
|
function applyFilterRow(row, filter) {
|
|
382
|
-
return Object.keys(filter).every(
|
|
383
|
-
|
|
314
|
+
return Object.keys(filter).every(filterKey => {
|
|
315
|
+
const operator = operators.get(filterKey);
|
|
384
316
|
if (operator) {
|
|
385
317
|
return operator(row, filter[filterKey]);
|
|
386
318
|
}
|
|
@@ -393,80 +325,65 @@ function ddfCsvReader(logger) {
|
|
|
393
325
|
});
|
|
394
326
|
}
|
|
395
327
|
function getJoinFilters(join, queryParam, options) {
|
|
396
|
-
return Promise.all(Object.keys(join).map(
|
|
397
|
-
.then(
|
|
328
|
+
return Promise.all(Object.keys(join).map(joinID => getJoinFilter(joinID, join[joinID], queryParam, options)))
|
|
329
|
+
.then(results => results.reduce(mergeObjects, {}));
|
|
398
330
|
}
|
|
399
331
|
function mergeObjects(a, b) {
|
|
400
332
|
return Object.assign(a, b);
|
|
401
333
|
}
|
|
402
334
|
function getJoinFilter(joinID, join, queryParam, options) {
|
|
403
|
-
var _a;
|
|
404
335
|
if (options.conceptsLookup.get(join.key).concept_type === 'time') {
|
|
405
|
-
return Promise.resolve(
|
|
336
|
+
return Promise.resolve({ [joinID]: join.where });
|
|
406
337
|
}
|
|
407
338
|
else {
|
|
408
339
|
return query({
|
|
409
340
|
select: { key: [join.key] },
|
|
410
341
|
where: join.where,
|
|
411
342
|
from: options.conceptsLookup.has(join.key) ? 'entities' : 'concepts'
|
|
412
|
-
}, Object.assign({ joinID
|
|
413
|
-
.then(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
_b),
|
|
421
|
-
_a);
|
|
422
|
-
});
|
|
343
|
+
}, Object.assign({ joinID }, options))
|
|
344
|
+
.then(result => ({
|
|
345
|
+
[joinID]: {
|
|
346
|
+
[join.key]: {
|
|
347
|
+
$in: new Set(result.map(row => row[join.key]))
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}));
|
|
423
351
|
}
|
|
424
352
|
}
|
|
425
353
|
function getFilterFields(filter) {
|
|
426
|
-
|
|
427
|
-
for (
|
|
354
|
+
const fields = [];
|
|
355
|
+
for (const field in filter) {
|
|
428
356
|
if (includes(['$and', '$or', '$not', '$nor'], field)) {
|
|
429
|
-
filter[field].map(getFilterFields).forEach(
|
|
357
|
+
filter[field].map(getFilterFields).forEach(subFields => fields.push(...subFields));
|
|
430
358
|
}
|
|
431
359
|
else {
|
|
432
360
|
fields.push(field);
|
|
433
361
|
}
|
|
434
362
|
}
|
|
435
|
-
return
|
|
363
|
+
return [...new Set(fields)];
|
|
436
364
|
}
|
|
437
365
|
function filterConceptsByType(conceptTypes, queryKey, options) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
var concept = options.conceptsLookup.get(conceptString);
|
|
445
|
-
if (includes(conceptTypes, concept.concept_type)) {
|
|
446
|
-
concepts.push(concept);
|
|
447
|
-
}
|
|
366
|
+
const conceptStrings = queryKey || Array.from(options.conceptsLookup.keys());
|
|
367
|
+
const concepts = [];
|
|
368
|
+
for (const conceptString of conceptStrings) {
|
|
369
|
+
const concept = options.conceptsLookup.get(conceptString);
|
|
370
|
+
if (includes(conceptTypes, concept.concept_type)) {
|
|
371
|
+
concepts.push(concept);
|
|
448
372
|
}
|
|
449
373
|
}
|
|
450
|
-
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
451
|
-
finally {
|
|
452
|
-
try {
|
|
453
|
-
if (conceptStrings_1_1 && !conceptStrings_1_1.done && (_a = conceptStrings_1.return)) _a.call(conceptStrings_1);
|
|
454
|
-
}
|
|
455
|
-
finally { if (e_3) throw e_3.error; }
|
|
456
|
-
}
|
|
457
374
|
return concepts;
|
|
458
375
|
}
|
|
459
376
|
function getEntityConceptRenameMap(queryKey, resourceKey, options) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
377
|
+
const resourceKeySet = new Set(resourceKey);
|
|
378
|
+
const entityConceptTypes = ['entity_set', 'entity_domain'];
|
|
379
|
+
const queryEntityConcepts = filterConceptsByType(entityConceptTypes, queryKey, options);
|
|
463
380
|
if (queryEntityConcepts.length === 0) {
|
|
464
381
|
return new Map();
|
|
465
382
|
}
|
|
466
|
-
|
|
383
|
+
const allEntityConcepts = filterConceptsByType(entityConceptTypes, null, options);
|
|
467
384
|
return queryEntityConcepts
|
|
468
|
-
.map(
|
|
469
|
-
.filter(
|
|
385
|
+
.map(concept => allEntityConcepts
|
|
386
|
+
.filter(lookupConcept => {
|
|
470
387
|
if (concept.concept_type === 'entity_set') {
|
|
471
388
|
return resourceKeySet.has(lookupConcept.concept) &&
|
|
472
389
|
lookupConcept.concept !== concept.concept &&
|
|
@@ -479,114 +396,86 @@ function ddfCsvReader(logger) {
|
|
|
479
396
|
lookupConcept.domain === concept.concept;
|
|
480
397
|
}
|
|
481
398
|
})
|
|
482
|
-
.reduce(
|
|
399
|
+
.reduce((map, aliasConcept) => map.set(aliasConcept.concept, concept.concept), new Map())).reduce((mapA, mapB) => new Map([...mapA, ...mapB]), new Map());
|
|
483
400
|
}
|
|
484
401
|
function getEntitySetFilter(conceptStrings, queryParam, options) {
|
|
485
|
-
|
|
486
|
-
.map(
|
|
402
|
+
const promises = filterConceptsByType(['entity_set'], conceptStrings, options)
|
|
403
|
+
.map(concept => query({
|
|
487
404
|
select: { key: [concept.domain], value: ['is--' + concept.concept] },
|
|
488
405
|
from: 'entities'
|
|
489
|
-
}, Object.assign({},
|
|
490
|
-
.then(
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}); });
|
|
500
|
-
return Promise.all(promises).then(function (results) {
|
|
501
|
-
return results.reduce(function (a, b) { return Object.assign(a, b); }, {});
|
|
406
|
+
}, Object.assign({}, options))
|
|
407
|
+
.then(result => ({
|
|
408
|
+
[concept.concept]: {
|
|
409
|
+
$in: new Set(result
|
|
410
|
+
.filter(row => row['is--' + concept.concept])
|
|
411
|
+
.map(row => row[concept.domain]))
|
|
412
|
+
}
|
|
413
|
+
})));
|
|
414
|
+
return Promise.all(promises).then(results => {
|
|
415
|
+
return results.reduce((a, b) => Object.assign(a, b), {});
|
|
502
416
|
});
|
|
503
417
|
}
|
|
504
418
|
function getResources(key, value) {
|
|
505
419
|
if (!value || value.length === 0 || key[0] === value) {
|
|
506
|
-
return new Set(
|
|
507
|
-
|
|
508
|
-
|
|
420
|
+
return new Set([...keyValueLookup
|
|
421
|
+
.get(createKeyString(key))
|
|
422
|
+
.values()
|
|
423
|
+
].reduce((a, b) => a.concat(b)));
|
|
509
424
|
}
|
|
510
425
|
if (Array.isArray(value)) {
|
|
511
426
|
return value
|
|
512
|
-
.map(
|
|
513
|
-
.reduce(
|
|
427
|
+
.map(singleValue => getResources(key, singleValue))
|
|
428
|
+
.reduce((resultSet, resources) => new Set([...resultSet, ...resources]), new Set());
|
|
514
429
|
}
|
|
515
|
-
|
|
430
|
+
let oneKeyOneValueResourcesArray = keyValueLookup
|
|
516
431
|
.get(createKeyString(key))
|
|
517
432
|
.get(value);
|
|
518
433
|
if (oneKeyOneValueResourcesArray) {
|
|
519
434
|
oneKeyOneValueResourcesArray = oneKeyOneValueResourcesArray
|
|
520
|
-
.filter(
|
|
435
|
+
.filter(v => isEmpty(optimalFilesSet) || includes(optimalFilesSet, v.path));
|
|
521
436
|
}
|
|
522
437
|
return new Set(oneKeyOneValueResourcesArray);
|
|
523
438
|
}
|
|
524
439
|
function processResourceResponse(response, select, filterFields, options) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
440
|
+
const resourcePK = response.resource.schema.primaryKey;
|
|
441
|
+
const resourceProjection = new Set([...resourcePK, ...select.value, ...filterFields]);
|
|
442
|
+
const renameMap = getEntityConceptRenameMap(select.key, resourcePK, options);
|
|
528
443
|
return response.data
|
|
529
|
-
.map(
|
|
530
|
-
.map(
|
|
444
|
+
.map(row => projectRow(row, resourceProjection))
|
|
445
|
+
.map(row => renameHeaderRow(row, renameMap));
|
|
531
446
|
}
|
|
532
447
|
function loadResources(key, value, language, options, queryParam) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
debug('resources list by query', { queryParam
|
|
536
|
-
return Promise.all(
|
|
448
|
+
const { debug } = options.diagnostic.prepareDiagnosticFor('loadResource');
|
|
449
|
+
const resources = getResources(key, value);
|
|
450
|
+
debug('resources list by query', { queryParam, resources: [...resources] });
|
|
451
|
+
return Promise.all([...resources].map(resource => loadResource(resource, language, options)));
|
|
537
452
|
}
|
|
538
453
|
function projectRow(row, projectionSet) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
var concept = _c.value;
|
|
544
|
-
if (projectionSet.has(concept)) {
|
|
545
|
-
result[concept] = row[concept];
|
|
546
|
-
}
|
|
454
|
+
const result = {};
|
|
455
|
+
for (const concept of Object.keys(row)) {
|
|
456
|
+
if (projectionSet.has(concept)) {
|
|
457
|
+
result[concept] = row[concept];
|
|
547
458
|
}
|
|
548
459
|
}
|
|
549
|
-
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
550
|
-
finally {
|
|
551
|
-
try {
|
|
552
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
553
|
-
}
|
|
554
|
-
finally { if (e_4) throw e_4.error; }
|
|
555
|
-
}
|
|
556
460
|
return result;
|
|
557
461
|
}
|
|
558
462
|
function renameHeaderRow(row, renameMap) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
for (var _b = tslib_1.__values(Object.keys(row)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
563
|
-
var concept = _c.value;
|
|
564
|
-
result[renameMap.get(concept) || concept] = row[concept];
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
568
|
-
finally {
|
|
569
|
-
try {
|
|
570
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
571
|
-
}
|
|
572
|
-
finally { if (e_5) throw e_5.error; }
|
|
463
|
+
const result = {};
|
|
464
|
+
for (const concept of Object.keys(row)) {
|
|
465
|
+
result[renameMap.get(concept) || concept] = row[concept];
|
|
573
466
|
}
|
|
574
467
|
return result;
|
|
575
468
|
}
|
|
576
|
-
function joinData(key, joinMode) {
|
|
577
|
-
var data = [];
|
|
578
|
-
for (var _i = 2; _i < arguments.length; _i++) {
|
|
579
|
-
data[_i - 2] = arguments[_i];
|
|
580
|
-
}
|
|
469
|
+
function joinData(key, joinMode, ...data) {
|
|
581
470
|
if (data.length === 1) {
|
|
582
471
|
return data[0];
|
|
583
472
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
dataPar.forEach(
|
|
587
|
-
|
|
473
|
+
const canonicalKey = key.slice(0).sort();
|
|
474
|
+
const dataMap = data.reduce((result, dataPar) => {
|
|
475
|
+
dataPar.forEach(row => {
|
|
476
|
+
const keyString = canonicalKey.map(concept => row[concept]).join(',');
|
|
588
477
|
if (result.has(keyString)) {
|
|
589
|
-
|
|
478
|
+
const resultRow = result.get(keyString);
|
|
590
479
|
joinRow(resultRow, row, joinMode);
|
|
591
480
|
}
|
|
592
481
|
else {
|
|
@@ -595,7 +484,7 @@ function ddfCsvReader(logger) {
|
|
|
595
484
|
});
|
|
596
485
|
return result;
|
|
597
486
|
}, new Map());
|
|
598
|
-
return
|
|
487
|
+
return [...dataMap.values()];
|
|
599
488
|
}
|
|
600
489
|
function joinRow(resultRow, sourceRow, mode) {
|
|
601
490
|
switch (mode) {
|
|
@@ -603,18 +492,18 @@ function ddfCsvReader(logger) {
|
|
|
603
492
|
Object.assign(resultRow, sourceRow);
|
|
604
493
|
break;
|
|
605
494
|
case 'translation':
|
|
606
|
-
for (
|
|
495
|
+
for (const concept in sourceRow) {
|
|
607
496
|
if (sourceRow[concept] !== '') {
|
|
608
497
|
resultRow[concept] = sourceRow[concept];
|
|
609
498
|
}
|
|
610
499
|
}
|
|
611
500
|
break;
|
|
612
501
|
case 'overwriteWithError':
|
|
613
|
-
for (
|
|
502
|
+
for (const concept in sourceRow) {
|
|
614
503
|
if (resultRow[concept] !== undefined && resultRow[concept] !== sourceRow[concept]) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
504
|
+
const sourceRowStr = JSON.stringify(sourceRow);
|
|
505
|
+
const resultRowStr = JSON.stringify(resultRow);
|
|
506
|
+
const errStr = `JOIN Error: two resources have different data for "${concept}": ${sourceRowStr},${resultRowStr}`;
|
|
618
507
|
throwError(new ddfcsv_error_1.DdfCsvError(ddfcsv_error_1.DDF_ERROR, errStr));
|
|
619
508
|
}
|
|
620
509
|
else {
|
|
@@ -625,76 +514,75 @@ function ddfCsvReader(logger) {
|
|
|
625
514
|
}
|
|
626
515
|
}
|
|
627
516
|
function throwError(error) {
|
|
628
|
-
|
|
517
|
+
const currentLogger = logger || console;
|
|
629
518
|
currentLogger.error(error.message);
|
|
630
519
|
throw error;
|
|
631
520
|
}
|
|
632
|
-
function createKeyString(key, row) {
|
|
633
|
-
|
|
634
|
-
var canonicalKey = key.slice(0).sort();
|
|
521
|
+
function createKeyString(key, row = false) {
|
|
522
|
+
const canonicalKey = key.slice(0).sort();
|
|
635
523
|
if (!row) {
|
|
636
524
|
return canonicalKey.join(',');
|
|
637
525
|
}
|
|
638
526
|
else {
|
|
639
|
-
return canonicalKey.map(
|
|
527
|
+
return canonicalKey.map(concept => row[concept]).join(',');
|
|
640
528
|
}
|
|
641
529
|
}
|
|
642
530
|
function loadResource(resource, language, options) {
|
|
643
|
-
|
|
644
|
-
|
|
531
|
+
const { warning } = options.diagnostic.prepareDiagnosticFor('loadResource');
|
|
532
|
+
const filePromises = [];
|
|
645
533
|
if (typeof resource.data === 'undefined') {
|
|
646
534
|
resource.data = loadFile(resource.path, options);
|
|
647
535
|
}
|
|
648
536
|
filePromises.push(resource.data);
|
|
649
|
-
|
|
650
|
-
|
|
537
|
+
const languageValid = typeof language !== 'undefined' && includes(getLanguages(options), language);
|
|
538
|
+
const languageLoaded = typeof resource.translations[language] !== 'undefined';
|
|
651
539
|
if (languageValid) {
|
|
652
540
|
if (!languageLoaded) {
|
|
653
|
-
|
|
654
|
-
resource.translations[language] = loadFile(
|
|
655
|
-
.catch(
|
|
656
|
-
warning(
|
|
541
|
+
const translationPath = `lang/${language}/${resource.path}`;
|
|
542
|
+
resource.translations[language] = loadFile(translationPath, options)
|
|
543
|
+
.catch(err => {
|
|
544
|
+
warning(`translation file ${translationPath}`, err);
|
|
657
545
|
return Promise.resolve({});
|
|
658
546
|
});
|
|
659
547
|
}
|
|
660
548
|
filePromises.push(resource.translations[language]);
|
|
661
549
|
}
|
|
662
|
-
return Promise.all(filePromises).then(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
return { data
|
|
550
|
+
return Promise.all(filePromises).then(fileResponses => {
|
|
551
|
+
const filesData = fileResponses.map(resp => resp.data || []);
|
|
552
|
+
const primaryKey = resource.schema.primaryKey;
|
|
553
|
+
const data = joinData(primaryKey, 'translation', ...filesData);
|
|
554
|
+
return { data, resource };
|
|
667
555
|
});
|
|
668
556
|
}
|
|
669
557
|
function getLanguages(options) {
|
|
670
558
|
if (!options.datapackage.translations) {
|
|
671
559
|
return [];
|
|
672
560
|
}
|
|
673
|
-
return options.datapackage.translations.map(
|
|
561
|
+
return options.datapackage.translations.map(lang => lang.id);
|
|
674
562
|
}
|
|
675
563
|
function loadFile(filePath, options) {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
debug(
|
|
679
|
-
return new Promise(
|
|
680
|
-
options.fileReader.readText(fullFilePath,
|
|
564
|
+
const { debug, error } = options.diagnostic.prepareDiagnosticFor('loadFile');
|
|
565
|
+
const fullFilePath = (0, ddf_query_validator_1.getFilePath)(options.basePath, filePath);
|
|
566
|
+
debug(`start reading "${filePath}"`);
|
|
567
|
+
return new Promise((resolve, reject) => {
|
|
568
|
+
options.fileReader.readText(fullFilePath, (err, data) => {
|
|
681
569
|
if (err) {
|
|
682
|
-
error(
|
|
570
|
+
error(`fail "${filePath}" reading`, err);
|
|
683
571
|
return reject(new ddfcsv_error_1.DdfCsvError(ddfcsv_error_1.FILE_READING_ERROR, err, fullFilePath));
|
|
684
572
|
}
|
|
685
573
|
Papa.parse(stripBom(data), {
|
|
686
574
|
header: true,
|
|
687
575
|
skipEmptyLines: true,
|
|
688
|
-
dynamicTyping:
|
|
689
|
-
|
|
690
|
-
return includes(['
|
|
576
|
+
dynamicTyping: (headerName) => {
|
|
577
|
+
const concept = options.conceptsLookup.get(headerName) || {};
|
|
578
|
+
return !includes(['time', 'string'], concept.concept_type);
|
|
691
579
|
},
|
|
692
|
-
complete:
|
|
693
|
-
debug(
|
|
580
|
+
complete: result => {
|
|
581
|
+
debug(`finish reading "${filePath}"`);
|
|
694
582
|
resolve(result);
|
|
695
583
|
},
|
|
696
|
-
error:
|
|
697
|
-
error(
|
|
584
|
+
error: parseErr => {
|
|
585
|
+
error(`fail "${filePath}" parsing`, parseErr);
|
|
698
586
|
reject(new ddfcsv_error_1.DdfCsvError(ddfcsv_error_1.CSV_PARSING_ERROR, parseErr, filePath));
|
|
699
587
|
}
|
|
700
588
|
});
|
|
@@ -705,10 +593,18 @@ function ddfCsvReader(logger) {
|
|
|
705
593
|
if (resourcesLookup.size > 0) {
|
|
706
594
|
return resourcesLookup;
|
|
707
595
|
}
|
|
708
|
-
datapackagePar.resources.forEach(
|
|
596
|
+
datapackagePar.resources.forEach(resource => {
|
|
709
597
|
if (!Array.isArray(resource.schema.primaryKey)) {
|
|
710
598
|
resource.schema.primaryKey = [resource.schema.primaryKey];
|
|
711
599
|
}
|
|
600
|
+
const constraints = resource.schema.fields.reduce((result, field) => {
|
|
601
|
+
var _a;
|
|
602
|
+
if ((_a = field.constraints) === null || _a === void 0 ? void 0 : _a.enum) {
|
|
603
|
+
result[field.name] = field.constraints.enum;
|
|
604
|
+
}
|
|
605
|
+
return result;
|
|
606
|
+
}, {});
|
|
607
|
+
resource.constraints = constraints;
|
|
712
608
|
resource.translations = {};
|
|
713
609
|
resourcesLookup.set(resource.name, resource);
|
|
714
610
|
});
|
|
@@ -718,10 +614,10 @@ function ddfCsvReader(logger) {
|
|
|
718
614
|
if (keyValueLookup.size > 0) {
|
|
719
615
|
return keyValueLookup;
|
|
720
616
|
}
|
|
721
|
-
for (
|
|
722
|
-
datapackagePar.ddfSchema[collection].map(
|
|
723
|
-
|
|
724
|
-
|
|
617
|
+
for (const collection in datapackagePar.ddfSchema) {
|
|
618
|
+
datapackagePar.ddfSchema[collection].map(kvPair => {
|
|
619
|
+
const key = createKeyString(kvPair.primaryKey);
|
|
620
|
+
const resources = kvPair.resources.map(resourceName => resourcesLookup.get(resourceName));
|
|
725
621
|
if (keyValueLookup.has(key)) {
|
|
726
622
|
keyValueLookup.get(key).set(kvPair.value, resources);
|
|
727
623
|
}
|
|
@@ -733,7 +629,8 @@ function ddfCsvReader(logger) {
|
|
|
733
629
|
return keyValueLookup;
|
|
734
630
|
}
|
|
735
631
|
return {
|
|
736
|
-
query
|
|
632
|
+
query,
|
|
633
|
+
loadFile
|
|
737
634
|
};
|
|
738
635
|
}
|
|
739
636
|
exports.ddfCsvReader = ddfCsvReader;
|