@larsgw/formica 0.10.1 → 0.10.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 +15 -3
- package/lib/bin/check-diff.js +76 -0
- package/lib/bin/check-resources-diff.d.ts +2 -0
- package/lib/bin/check-resources-diff.js +101 -0
- package/lib/bin/diff-check.js +76 -0
- package/lib/bin/process-resources.d.ts +0 -4
- package/lib/bin/process-resources.js +138 -28
- package/lib/bin/validate-catalog.js +9 -9
- package/lib/bin/validate-integrity.d.ts +2 -0
- package/lib/bin/validate-integrity.js +133 -0
- package/lib/taxon-names/index.d.ts +6 -1
- package/lib/taxon-names/index.js +91 -21
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -20,13 +20,25 @@ Run locally:
|
|
|
20
20
|
- `loir-validate-resources [./B1234.txt]`:
|
|
21
21
|
Validate the `.txt` files containing information on the taxonomic scope of keys
|
|
22
22
|
([documentation](https://github.com/identification-resources/catalog/blob/main/docs/resources-txt.md)).
|
|
23
|
+
- `loir-validate-integrity .`:
|
|
24
|
+
Check if all values are mapped as expected.
|
|
23
25
|
- `loir-resources-process [./resources]`:
|
|
24
26
|
Interactive tool to convert the aforementioned `.txt` files to Darwin Core archives
|
|
25
27
|
([documentation](https://github.com/identification-resources/catalog/blob/main/docs/resources-dwc.md)),
|
|
26
|
-
and link the taxa to GBIF and Catalogue of Life identifiers.
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
and link the taxa to GBIF and Catalogue of Life identifiers.
|
|
29
|
+
- Use `-s` or `--source` to specify which files should be processed: `unprocessed`
|
|
30
|
+
(default) for `.txt` files without corresponding Darwin Core files; `modified`
|
|
31
|
+
if in a Git repo to update existing Darwin Core files while keeping the identifiers
|
|
32
|
+
stable (in most cases); `all` for all `.txt` files; or `dwc` for only update
|
|
33
|
+
identifier mappings.
|
|
34
|
+
- Use `-u` or `--update-mappings` to specify which mappings to update: `col` or
|
|
35
|
+
`gbif`. This argument can be repeated. By default, both COL and GBIF are updated.
|
|
36
|
+
- Use `-k` or `--keep-mappings` to not update any mappings. Cannot be used in
|
|
37
|
+
conjunction with `-u`.
|
|
38
|
+
- Use `--mapping-api` to use either `gnverifier` (default) or `clb`, the ChecklistBank
|
|
39
|
+
API, to update the mappings.
|
|
29
40
|
- `loir-resources-index [./resources]`: Create indices of the Darwin Core archives.
|
|
41
|
+
- `loir-resources-check-diff [./resources/dwc]`: Determine which columns changed.
|
|
30
42
|
|
|
31
43
|
## API
|
|
32
44
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
// import { promises as fs, existsSync as doesFileExist } from 'fs'
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const util = __importStar(require("util"));
|
|
49
|
+
const util_1 = require("./util");
|
|
50
|
+
function listChangedFiles(directory) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const output = yield (0, util_1.runCommand)('git', ['diff', '--name-only', 'HEAD', '--', directory], {
|
|
53
|
+
cwd: directory
|
|
54
|
+
});
|
|
55
|
+
return output.trimEnd().split('\n');
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function getOldFile(file) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
const options = {
|
|
61
|
+
cwd: path.dirname(file)
|
|
62
|
+
};
|
|
63
|
+
const gitRoot = (yield (0, util_1.runCommand)('git', ['rev-parse', '--show-toplevel'], options)).trim();
|
|
64
|
+
return yield (0, util_1.runCommand)('git', ['show', 'HEAD:' + path.relative(gitRoot, file)], options);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function main() {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const args = util.parseArgs({
|
|
70
|
+
allowPositionals: true
|
|
71
|
+
});
|
|
72
|
+
const files = yield listChangedFiles(path.resolve(args.positionals[0]));
|
|
73
|
+
console.log(files);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const util = __importStar(require("util"));
|
|
49
|
+
const index_1 = require("../index");
|
|
50
|
+
const util_1 = require("./util");
|
|
51
|
+
function listChangedFiles(directory) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
const output = yield (0, util_1.runCommand)('git', ['diff', '--name-only', 'HEAD', '--', directory], {
|
|
54
|
+
cwd: directory
|
|
55
|
+
});
|
|
56
|
+
return output.trimEnd().split('\n');
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function getOldFile(file, gitRoot) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
return (0, util_1.runCommand)('git', ['show', 'HEAD:' + path.relative(gitRoot, file)], {
|
|
62
|
+
cwd: path.dirname(file)
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function main() {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
var _a;
|
|
69
|
+
const args = util.parseArgs({
|
|
70
|
+
allowPositionals: true
|
|
71
|
+
});
|
|
72
|
+
const base = path.resolve(args.positionals[0]);
|
|
73
|
+
const files = yield listChangedFiles(base);
|
|
74
|
+
const gitRoot = (yield (0, util_1.runCommand)('git', ['rev-parse', '--show-toplevel'], {
|
|
75
|
+
cwd: args.positionals[0]
|
|
76
|
+
})).trim();
|
|
77
|
+
const columnIndex = {};
|
|
78
|
+
for (const fileName of files) {
|
|
79
|
+
const filePath = path.join(gitRoot, fileName);
|
|
80
|
+
const b = index_1.csv.parseCsv(yield fs_1.promises.readFile(filePath, 'utf8'));
|
|
81
|
+
const a = index_1.csv.parseCsv(yield getOldFile(filePath, gitRoot));
|
|
82
|
+
const header = a[0];
|
|
83
|
+
for (let i = 1; i < Math.max(a.length, b.length); i++) {
|
|
84
|
+
for (let j = 0; j < header.length; j++) {
|
|
85
|
+
if (!a[i] || !b[i] || a[i][j] !== b[i][j]) {
|
|
86
|
+
((_a = columnIndex[header[j]]) !== null && _a !== void 0 ? _a : (columnIndex[header[j]] = new Set())).add(path.basename(fileName, '.csv'));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const output = Object.entries(columnIndex)
|
|
92
|
+
.sort((a, b) => b[1].size - a[1].size)
|
|
93
|
+
.map(([column, files]) => ({
|
|
94
|
+
column,
|
|
95
|
+
count: files.size,
|
|
96
|
+
files: (files.size > 10 ? [...files].slice(0, 10).concat('...') : [...files]).join(', ')
|
|
97
|
+
}));
|
|
98
|
+
console.table(output);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
// import { promises as fs, existsSync as doesFileExist } from 'fs'
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const util = __importStar(require("util"));
|
|
49
|
+
const util_1 = require("./util");
|
|
50
|
+
function listChangedFiles(directory) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const output = yield (0, util_1.runCommand)('git', ['diff', '--name-only', 'HEAD', '--', directory], {
|
|
53
|
+
cwd: directory
|
|
54
|
+
});
|
|
55
|
+
return output.trimEnd().split('\n');
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function getOldFile(file) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
const options = {
|
|
61
|
+
cwd: path.dirname(file)
|
|
62
|
+
};
|
|
63
|
+
const gitRoot = (yield (0, util_1.runCommand)('git', ['rev-parse', '--show-toplevel'], options)).trim();
|
|
64
|
+
return yield (0, util_1.runCommand)('git', ['show', 'HEAD:' + path.relative(gitRoot, file)], options);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function main() {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const args = util.parseArgs({
|
|
70
|
+
allowPositionals: true
|
|
71
|
+
});
|
|
72
|
+
const files = yield listChangedFiles(args.positionals[0]);
|
|
73
|
+
console.log(files);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
main().catch(console.error);
|
|
@@ -43,18 +43,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
});
|
|
44
44
|
};
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
exports.ResourceProcessorSource =
|
|
46
|
+
exports.ResourceProcessorSource = void 0;
|
|
47
47
|
const fs_1 = require("fs");
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
49
|
const child_process_1 = require("child_process");
|
|
50
50
|
const util = __importStar(require("util"));
|
|
51
51
|
const index_1 = require("../index");
|
|
52
52
|
const util_1 = require("./util");
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
const { ResourceTaxonIdSource, formatClassificationPath } = index_1.taxonNames;
|
|
54
|
+
var ResourceTaxonIdApi;
|
|
55
|
+
(function (ResourceTaxonIdApi) {
|
|
56
|
+
ResourceTaxonIdApi["GNVerifier"] = "gnverifier";
|
|
57
|
+
ResourceTaxonIdApi["ChecklistBank"] = "clb";
|
|
58
|
+
})(ResourceTaxonIdApi || (ResourceTaxonIdApi = {}));
|
|
58
59
|
var ResourceProcessorSource;
|
|
59
60
|
(function (ResourceProcessorSource) {
|
|
60
61
|
ResourceProcessorSource["All"] = "all";
|
|
@@ -137,6 +138,55 @@ function runGnverifier(names, sources) {
|
|
|
137
138
|
proc.stdin.end();
|
|
138
139
|
});
|
|
139
140
|
}
|
|
141
|
+
const CLB_SOURCES = {
|
|
142
|
+
gbif: '53147',
|
|
143
|
+
col: '3LXR'
|
|
144
|
+
};
|
|
145
|
+
function getBatchedFetch(limit = 20) {
|
|
146
|
+
const queue = [];
|
|
147
|
+
let count = 0;
|
|
148
|
+
return function (...args) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
if (count >= limit) {
|
|
151
|
+
yield new Promise(resolve => { queue.push(resolve); });
|
|
152
|
+
}
|
|
153
|
+
count++;
|
|
154
|
+
try {
|
|
155
|
+
return yield fetch(...args);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
count--;
|
|
159
|
+
if (count < limit) {
|
|
160
|
+
const resolver = queue.shift();
|
|
161
|
+
if (resolver) {
|
|
162
|
+
resolver();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function runClbNameParser(taxa, sources) {
|
|
170
|
+
const fetchBatched = getBatchedFetch(50);
|
|
171
|
+
const promises = [];
|
|
172
|
+
for (const id in taxa) {
|
|
173
|
+
const { scientificName, scientificNameAuthorship: authorship, taxonRank: rank } = taxa[id];
|
|
174
|
+
const params = new URLSearchParams({
|
|
175
|
+
scientificName: authorship ? scientificName.slice(0, -1 - authorship.length) : scientificName,
|
|
176
|
+
authorship: authorship !== null && authorship !== void 0 ? authorship : '',
|
|
177
|
+
rank: rank !== null && rank !== void 0 ? rank : ''
|
|
178
|
+
});
|
|
179
|
+
const queryString = params.toString();
|
|
180
|
+
for (const source of sources) {
|
|
181
|
+
const url = `https://api.checklistbank.org/dataset/${CLB_SOURCES[source]}/match/nameusage?verbose=true&${queryString}`;
|
|
182
|
+
const promise = fetchBatched(url)
|
|
183
|
+
.then(response => response.text())
|
|
184
|
+
.then(response => [taxa[id].scientificNameID, source, response]);
|
|
185
|
+
promises.push(promise);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return Promise.all(promises);
|
|
189
|
+
}
|
|
140
190
|
function listFiles(directory) {
|
|
141
191
|
return __awaiter(this, void 0, void 0, function* () {
|
|
142
192
|
const input = yield fs_1.promises.readdir(directory);
|
|
@@ -349,6 +399,22 @@ class ResourceProcessor {
|
|
|
349
399
|
if (config.updateMappings.length === 0) {
|
|
350
400
|
return resource;
|
|
351
401
|
}
|
|
402
|
+
const filteredResults = config.mappingsApi === ResourceTaxonIdApi.ChecklistBank
|
|
403
|
+
? yield this.getClbNameParserResults(resource, config)
|
|
404
|
+
: yield this.getGnverifierResults(resource, config);
|
|
405
|
+
const { taxonNames: { amendResource, groupNameMatches } } = yield Promise.resolve().then(() => __importStar(require('../index')));
|
|
406
|
+
const groupedNameMatches = groupNameMatches(filteredResults);
|
|
407
|
+
const amendedResource = Object.assign(Object.assign({}, resource), { taxa: Object.assign({}, resource.taxa) });
|
|
408
|
+
for (const key in groupedNameMatches) {
|
|
409
|
+
const source = key;
|
|
410
|
+
const matches = yield this.selectPrefixes(resource, groupedNameMatches[source], source);
|
|
411
|
+
amendResource(amendedResource, source, matches);
|
|
412
|
+
}
|
|
413
|
+
return amendedResource;
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
getGnverifierResults(resource, config) {
|
|
417
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
352
418
|
const filteredResults = {};
|
|
353
419
|
const taxonNames = {};
|
|
354
420
|
const names = new Set();
|
|
@@ -385,6 +451,8 @@ class ResourceProcessor {
|
|
|
385
451
|
// GBIF species like "Nomada spec"
|
|
386
452
|
continue;
|
|
387
453
|
}
|
|
454
|
+
const classificationRanks = match.classificationRanks.split('|');
|
|
455
|
+
const classificationPath = match.classificationPath.split('|').map((name, i) => ({ name, rank: classificationRanks[i] }));
|
|
388
456
|
for (const loirId of taxonNames[name]) {
|
|
389
457
|
const taxon = resource.taxa[loirId];
|
|
390
458
|
if (source === 11 && !GBIF_RANKS.includes(taxon.taxonRank)) {
|
|
@@ -410,44 +478,82 @@ class ResourceProcessor {
|
|
|
410
478
|
filteredResults[loirId] = [];
|
|
411
479
|
}
|
|
412
480
|
filteredResults[loirId].push({
|
|
413
|
-
source,
|
|
481
|
+
source: source === 11 ? ResourceTaxonIdSource.GBIF : ResourceTaxonIdSource.COL,
|
|
414
482
|
id: match.recordId,
|
|
415
483
|
currentId: match.currentRecordId,
|
|
416
|
-
classificationPath
|
|
484
|
+
classificationPath
|
|
417
485
|
});
|
|
418
486
|
}
|
|
419
487
|
}
|
|
420
488
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
489
|
+
return filteredResults;
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
getClbNameParserResults(resource, config) {
|
|
493
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
494
|
+
const filteredResults = {};
|
|
495
|
+
const result = yield runClbNameParser(resource.taxa, config.updateMappings);
|
|
496
|
+
for (const [loirId, source, results] of result) {
|
|
497
|
+
const { match, type: matchType, usage, alternatives = [] } = JSON.parse(results);
|
|
498
|
+
if (!match) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
filteredResults[loirId] = [];
|
|
502
|
+
const matches = [usage, ...alternatives];
|
|
503
|
+
for (const match of matches) {
|
|
504
|
+
const isSynonym = match.status !== 'accepted' && match.status !== 'provisionally accepted';
|
|
505
|
+
const currentRank = isSynonym ? match.classification[0].rank : match.rank;
|
|
506
|
+
const currentName = isSynonym ? match.classification[0].name : match.name;
|
|
507
|
+
const currentId = isSynonym ? match.classification[0].id : match.id;
|
|
508
|
+
if (matchType === 'higherrank') {
|
|
509
|
+
// Rank mismatch
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (source === ResourceTaxonIdSource.GBIF && currentRank === 'species' && currentName.endsWith(' spec')) {
|
|
513
|
+
// GBIF species like "Nomada spec"
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
const taxon = resource.taxa[loirId];
|
|
517
|
+
if (source === ResourceTaxonIdSource.GBIF && !GBIF_RANKS.includes(taxon.taxonRank)) {
|
|
518
|
+
// Exclude GBIF matches for ranks that are not in GBIF
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
else if (source === ResourceTaxonIdSource.GBIF && !isSynonym && currentRank !== taxon.taxonRank) {
|
|
522
|
+
// Exclude matches with rank mismatches (only possible
|
|
523
|
+
// for non-synonyms).
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
filteredResults[loirId].push({
|
|
527
|
+
source,
|
|
528
|
+
id: match.id,
|
|
529
|
+
currentId: currentId,
|
|
530
|
+
classificationPath: match.classification.map(({ name, rank }) => ({ name, rank })).reverse()
|
|
531
|
+
});
|
|
532
|
+
}
|
|
427
533
|
}
|
|
428
|
-
return
|
|
534
|
+
return filteredResults;
|
|
429
535
|
});
|
|
430
536
|
}
|
|
431
537
|
selectPrefixes(resource, groupedNameMatches, source) {
|
|
432
538
|
return __awaiter(this, void 0, void 0, function* () {
|
|
433
|
-
const prefixes = Object.keys(groupedNameMatches
|
|
539
|
+
const prefixes = Object.keys(groupedNameMatches);
|
|
434
540
|
if (prefixes.length === 0) {
|
|
435
541
|
return {};
|
|
436
542
|
}
|
|
437
543
|
else if (prefixes.length === 1) {
|
|
438
|
-
return groupedNameMatches[
|
|
544
|
+
return groupedNameMatches[prefixes[0]];
|
|
439
545
|
}
|
|
440
546
|
// Count total mapped taxa
|
|
441
547
|
const mappedTaxa = {};
|
|
442
548
|
for (const prefix of prefixes) {
|
|
443
|
-
for (const taxon in groupedNameMatches[
|
|
549
|
+
for (const taxon in groupedNameMatches[prefix]) {
|
|
444
550
|
mappedTaxa[taxon] = true;
|
|
445
551
|
}
|
|
446
552
|
}
|
|
447
|
-
const missedTaxonCount = Object.keys(mappedTaxa).length - Object.keys(groupedNameMatches[
|
|
553
|
+
const missedTaxonCount = Object.keys(mappedTaxa).length - Object.keys(groupedNameMatches[prefixes[0]]).length;
|
|
448
554
|
if (missedTaxonCount === 0) {
|
|
449
555
|
// Multiple prefixes but the first one maps all taxa (not counting that are unmapped in all prefixes)
|
|
450
|
-
return groupedNameMatches[
|
|
556
|
+
return groupedNameMatches[prefixes[0]];
|
|
451
557
|
}
|
|
452
558
|
console.error(`${resource.workId}: source ${source} results in multiple prefixes`);
|
|
453
559
|
let choice;
|
|
@@ -455,21 +561,17 @@ class ResourceProcessor {
|
|
|
455
561
|
console.error(` Most common prefix misses ${missedTaxonCount} taxa: automatically selecting most common prefix...`);
|
|
456
562
|
choice = '1';
|
|
457
563
|
}
|
|
458
|
-
else if (source === '1') {
|
|
459
|
-
console.error(` Catalogue of Life: automatically selecting most common prefix...`);
|
|
460
|
-
choice = '1';
|
|
461
|
-
}
|
|
462
564
|
else {
|
|
463
565
|
for (let i = 0; i < prefixes.length; i++) {
|
|
464
566
|
const prefix = prefixes[i];
|
|
465
|
-
const taxa = groupedNameMatches[
|
|
567
|
+
const taxa = groupedNameMatches[prefix];
|
|
466
568
|
const taxonIds = Object.keys(taxa);
|
|
467
569
|
console.error(` [${i + 1}] ${prefix} (${taxonIds.length} taxa)`);
|
|
468
570
|
for (let j = 0; j < Math.min(9, taxonIds.length); j++) {
|
|
469
571
|
const taxonId = taxonIds[j];
|
|
470
572
|
const taxon = resource.taxa[taxonId];
|
|
471
573
|
const match = taxa[taxonId];
|
|
472
|
-
console.error(` taxon: ${taxonId} "${taxon.scientificName}" - ${match.classificationPath
|
|
574
|
+
console.error(` taxon: ${taxonId} "${taxon.scientificName}" - ${formatClassificationPath(match.classificationPath)}`);
|
|
473
575
|
}
|
|
474
576
|
if (taxonIds.length > 9) {
|
|
475
577
|
console.error(` ...`);
|
|
@@ -486,7 +588,7 @@ class ResourceProcessor {
|
|
|
486
588
|
const matches = {};
|
|
487
589
|
for (const i of choice.split(',')) {
|
|
488
590
|
const prefix = prefixes[parseInt(i) - 1];
|
|
489
|
-
const taxa = groupedNameMatches[
|
|
591
|
+
const taxa = groupedNameMatches[prefix];
|
|
490
592
|
for (const id in taxa) {
|
|
491
593
|
if (id in matches) {
|
|
492
594
|
continue;
|
|
@@ -540,6 +642,10 @@ function main() {
|
|
|
540
642
|
type: 'string',
|
|
541
643
|
short: 'u',
|
|
542
644
|
multiple: true
|
|
645
|
+
},
|
|
646
|
+
'mapping-api': {
|
|
647
|
+
type: 'string',
|
|
648
|
+
default: 'gnverifier'
|
|
543
649
|
}
|
|
544
650
|
},
|
|
545
651
|
allowPositionals: true
|
|
@@ -563,6 +669,9 @@ function main() {
|
|
|
563
669
|
}
|
|
564
670
|
updateMappings = args.values['update-mappings'];
|
|
565
671
|
}
|
|
672
|
+
if (!Object.values(ResourceTaxonIdApi).includes(args.values['mapping-api'])) {
|
|
673
|
+
throw new Error(`Unknown argument "--mapping-api ${args.values['mapping-api']}"`);
|
|
674
|
+
}
|
|
566
675
|
const processor = new ResourceProcessor(args.positionals[0]);
|
|
567
676
|
process.on('exit', () => {
|
|
568
677
|
process.stdout.write('\n');
|
|
@@ -570,7 +679,8 @@ function main() {
|
|
|
570
679
|
const source = args.values.source;
|
|
571
680
|
const config = {
|
|
572
681
|
update: source === ResourceProcessorSource.All || source === ResourceProcessorSource.Modified,
|
|
573
|
-
updateMappings
|
|
682
|
+
updateMappings,
|
|
683
|
+
mappingsApi: args.values['mapping-api']
|
|
574
684
|
};
|
|
575
685
|
processor.run(source, config).catch(error => {
|
|
576
686
|
console.error(error);
|
|
@@ -104,15 +104,15 @@ class GbifTaxaValidator extends TaxaValidator {
|
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
class ColTaxaValidator extends TaxaValidator {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
107
|
+
// class ColTaxaValidator extends TaxaValidator {
|
|
108
|
+
// constructor (taxa: catalog.Entities) {
|
|
109
|
+
// super(taxa, {
|
|
110
|
+
// id: 'col',
|
|
111
|
+
// children: 'children_col',
|
|
112
|
+
// ancestors: 'ancestors_col'
|
|
113
|
+
// })
|
|
114
|
+
// }
|
|
115
|
+
// }
|
|
116
116
|
function validateFile(arg) {
|
|
117
117
|
return __awaiter(this, void 0, void 0, function* () {
|
|
118
118
|
const filePath = path.resolve(arg);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const index_1 = require("../index");
|
|
49
|
+
function loadCatalogFile(file, base) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
const contents = yield fs_1.promises.readFile(path.join(base, `${file}.csv`), 'utf8');
|
|
52
|
+
return index_1.catalog.loadData(contents, file);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function loadResourcesIndex(base) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
const contents = yield fs_1.promises.readFile(path.join(base, 'resources', 'index.json'), 'utf8');
|
|
58
|
+
return JSON.parse(contents);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function getAll(entity, field) {
|
|
62
|
+
const value = entity.get(field);
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
else if (value) {
|
|
67
|
+
return [value];
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function checkIntegrity(catalog, resources, field, sheet) {
|
|
74
|
+
const errors = [];
|
|
75
|
+
const index = new Set();
|
|
76
|
+
for (const entity of sheet) {
|
|
77
|
+
for (const value of getAll(entity, 'name')) {
|
|
78
|
+
index.add(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const entity of catalog) {
|
|
82
|
+
const id = entity.get('id');
|
|
83
|
+
for (const value of getAll(entity, field)) {
|
|
84
|
+
if (!index.has(value)) {
|
|
85
|
+
errors.push({
|
|
86
|
+
entity: id,
|
|
87
|
+
field: field,
|
|
88
|
+
error: `Value "${value}" not mapped`
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const id in resources) {
|
|
94
|
+
const resource = resources[id];
|
|
95
|
+
if (!resource.catalog || !Object.prototype.hasOwnProperty.call(resource.catalog, field)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const value = resource.catalog[field];
|
|
99
|
+
const values = Array.isArray(value) ? value : [value];
|
|
100
|
+
for (const value of values) {
|
|
101
|
+
if (!index.has(value)) {
|
|
102
|
+
errors.push({
|
|
103
|
+
entity: id,
|
|
104
|
+
field: field,
|
|
105
|
+
error: `Value "${value}" is not mapped`
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return errors;
|
|
111
|
+
}
|
|
112
|
+
function main(args) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
const base = path.resolve(args[0]);
|
|
115
|
+
const [catalog, authors, places, publishers, taxa, resources] = yield Promise.all([
|
|
116
|
+
loadCatalogFile('catalog', base),
|
|
117
|
+
loadCatalogFile('authors', base),
|
|
118
|
+
loadCatalogFile('places', base),
|
|
119
|
+
loadCatalogFile('publishers', base),
|
|
120
|
+
loadCatalogFile('taxa', base),
|
|
121
|
+
loadResourcesIndex(base)
|
|
122
|
+
]);
|
|
123
|
+
const errors = [
|
|
124
|
+
...checkIntegrity(catalog, resources, 'author', authors),
|
|
125
|
+
...checkIntegrity(catalog, resources, 'publisher', publishers),
|
|
126
|
+
...checkIntegrity(catalog, resources, 'taxon', taxa),
|
|
127
|
+
...checkIntegrity(catalog, resources, 'region', places)
|
|
128
|
+
];
|
|
129
|
+
console.table(errors);
|
|
130
|
+
process.exit(errors.length ? 1 : 0);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
main(process.argv.slice(2));
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
export declare enum ResourceTaxonIdSource {
|
|
2
|
+
COL = "col",
|
|
3
|
+
GBIF = "gbif"
|
|
4
|
+
}
|
|
5
|
+
export declare function formatClassificationPath(path: TaxonMatchClassificationPath): string;
|
|
1
6
|
export declare function groupNameMatches(results: Record<TaxonId, TaxonMatch[]>): GroupedNameMatches;
|
|
2
|
-
export declare function amendResource(resource: AmendedResource, source:
|
|
7
|
+
export declare function amendResource(resource: AmendedResource, source: ResourceTaxonIdSource, matches: Record<TaxonId, TaxonMatch>): void;
|
package/lib/taxon-names/index.js
CHANGED
|
@@ -1,38 +1,108 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResourceTaxonIdSource = void 0;
|
|
4
|
+
exports.formatClassificationPath = formatClassificationPath;
|
|
3
5
|
exports.groupNameMatches = groupNameMatches;
|
|
4
6
|
exports.amendResource = amendResource;
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
|
|
7
|
+
const RANKS = [
|
|
8
|
+
'superdomain',
|
|
9
|
+
'domain',
|
|
10
|
+
'kingdom',
|
|
11
|
+
'subkingdom',
|
|
12
|
+
'phylum',
|
|
13
|
+
'subphylum',
|
|
14
|
+
'infraphylum',
|
|
15
|
+
'parvphylum',
|
|
16
|
+
'gigaclass',
|
|
17
|
+
'megaclass',
|
|
18
|
+
'superclass',
|
|
19
|
+
'class',
|
|
20
|
+
'subclass',
|
|
21
|
+
'infraclass',
|
|
22
|
+
'subterclass',
|
|
23
|
+
'superorder',
|
|
24
|
+
'order',
|
|
25
|
+
'suborder',
|
|
26
|
+
'infraorder',
|
|
27
|
+
'parvorder',
|
|
28
|
+
'section zoology',
|
|
29
|
+
'subsection zoology',
|
|
30
|
+
'series zoology',
|
|
31
|
+
'superfamily',
|
|
32
|
+
'epifamily',
|
|
33
|
+
'family',
|
|
34
|
+
'subfamily',
|
|
35
|
+
'tribe',
|
|
36
|
+
'subtribe',
|
|
37
|
+
'genus',
|
|
38
|
+
'subgenus',
|
|
39
|
+
'section',
|
|
40
|
+
'group',
|
|
41
|
+
'aggregate',
|
|
42
|
+
'complex',
|
|
43
|
+
'species',
|
|
44
|
+
'subspecies',
|
|
45
|
+
'variety',
|
|
46
|
+
'form'
|
|
47
|
+
];
|
|
48
|
+
const MINIMUM_PREFIX_RANK = 'class';
|
|
49
|
+
const VALID_COMMON_PREFIXES = {
|
|
50
|
+
col: new Set([
|
|
51
|
+
'Eukaryota|Animalia|Mollusca',
|
|
52
|
+
'Eukaryota|Plantae|Bryobiotina|Bryophyta',
|
|
53
|
+
'Eukaryota|Plantae|Pteridobiotina|Tracheophyta',
|
|
54
|
+
'Eukaryota|Fungi',
|
|
55
|
+
'Eukaryota|Fungi|Ascomycota',
|
|
56
|
+
'Eukaryota|Fungi|Basidiomycota',
|
|
57
|
+
'Eukaryota|Fungi|Zygomycota'
|
|
58
|
+
]),
|
|
59
|
+
gbif: new Set([
|
|
60
|
+
'Plantae|Tracheophyta',
|
|
61
|
+
'Fungi',
|
|
62
|
+
'Fungi|Ascomycota',
|
|
63
|
+
'Fungi|Basidiomycota',
|
|
64
|
+
'Fungi|Zygomycota'
|
|
65
|
+
])
|
|
66
|
+
};
|
|
67
|
+
function compareRanks(a, b) {
|
|
68
|
+
return RANKS.indexOf(a) - RANKS.indexOf(b);
|
|
69
|
+
}
|
|
13
70
|
function getCommonPrefix(a, b) {
|
|
14
71
|
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
15
|
-
if (a[i] !== b[i]) {
|
|
72
|
+
if (a[i] && b[i] && a[i].name !== b[i].name) {
|
|
16
73
|
return a.slice(0, i);
|
|
17
74
|
}
|
|
18
75
|
}
|
|
19
76
|
return a.slice();
|
|
20
77
|
}
|
|
21
|
-
function isValidPrefix(a, b) {
|
|
78
|
+
function isValidPrefix(a, b, source) {
|
|
22
79
|
const prefix = getCommonPrefix(a, b);
|
|
23
|
-
|
|
80
|
+
if (prefix.length === 0) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (VALID_COMMON_PREFIXES[source].has(formatClassificationPath(prefix))) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return compareRanks(prefix[prefix.length - 1].rank, MINIMUM_PREFIX_RANK) >= 0;
|
|
87
|
+
}
|
|
88
|
+
var ResourceTaxonIdSource;
|
|
89
|
+
(function (ResourceTaxonIdSource) {
|
|
90
|
+
ResourceTaxonIdSource["COL"] = "col";
|
|
91
|
+
ResourceTaxonIdSource["GBIF"] = "gbif";
|
|
92
|
+
})(ResourceTaxonIdSource || (exports.ResourceTaxonIdSource = ResourceTaxonIdSource = {}));
|
|
93
|
+
function formatClassificationPath(path) {
|
|
94
|
+
return path.map(part => part.name).join('|');
|
|
24
95
|
}
|
|
25
96
|
function groupNameMatches(results) {
|
|
97
|
+
var _a;
|
|
26
98
|
const prefixes = {};
|
|
27
99
|
for (const scientificNameID in results) {
|
|
28
100
|
for (const result of results[scientificNameID]) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
let prefix = prefixes[result.source].find(prefix => isValidPrefix(prefix[0], result.classificationPath));
|
|
101
|
+
const matchGroups = (_a = prefixes[result.source]) !== null && _a !== void 0 ? _a : (prefixes[result.source] = []);
|
|
102
|
+
let prefix = matchGroups.find(prefix => isValidPrefix(prefix[0], result.classificationPath, result.source));
|
|
33
103
|
if (!prefix) {
|
|
34
104
|
prefix = [result.classificationPath, {}];
|
|
35
|
-
|
|
105
|
+
matchGroups.push(prefix);
|
|
36
106
|
}
|
|
37
107
|
else {
|
|
38
108
|
prefix[0] = getCommonPrefix(prefix[0], result.classificationPath);
|
|
@@ -44,11 +114,11 @@ function groupNameMatches(results) {
|
|
|
44
114
|
}
|
|
45
115
|
}
|
|
46
116
|
const groupedNameMatches = {};
|
|
47
|
-
for (const source
|
|
48
|
-
groupedNameMatches[source] =
|
|
117
|
+
for (const [source, matchGroups] of Object.entries(prefixes)) {
|
|
118
|
+
groupedNameMatches[source] = matchGroups
|
|
49
119
|
.sort((a, b) => Object.keys(b[1]).length - Object.keys(a[1]).length)
|
|
50
120
|
.reduce((map, [prefix, taxa]) => {
|
|
51
|
-
map[prefix
|
|
121
|
+
map[formatClassificationPath(prefix)] = taxa;
|
|
52
122
|
return map;
|
|
53
123
|
}, {});
|
|
54
124
|
}
|
|
@@ -57,13 +127,13 @@ function groupNameMatches(results) {
|
|
|
57
127
|
function amendResource(resource, source, matches) {
|
|
58
128
|
for (const id in matches) {
|
|
59
129
|
const match = matches[id];
|
|
60
|
-
if (source ===
|
|
130
|
+
if (source === ResourceTaxonIdSource.COL) {
|
|
61
131
|
resource.taxa[id].colTaxonID = match.id;
|
|
62
132
|
if (match.currentId) {
|
|
63
133
|
resource.taxa[id].colAcceptedTaxonID = match.currentId;
|
|
64
134
|
}
|
|
65
135
|
}
|
|
66
|
-
else if (source ===
|
|
136
|
+
else if (source === ResourceTaxonIdSource.GBIF) {
|
|
67
137
|
resource.taxa[id].gbifTaxonID = match.id;
|
|
68
138
|
if (match.currentId) {
|
|
69
139
|
resource.taxa[id].gbifAcceptedTaxonID = match.currentId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larsgw/formica",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "SDK and tools for data from the Library of Identification Resources",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
"bin": {
|
|
11
11
|
"loir-validate-catalog": "lib/bin/validate-catalog.js",
|
|
12
12
|
"loir-validate-resources": "lib/bin/validate-resources-text.js",
|
|
13
|
+
"loir-validate-integrity": "lib/bin/validate-integrity.js",
|
|
13
14
|
"loir-resources-process": "lib/bin/process-resources.js",
|
|
14
15
|
"loir-resources-index": "lib/bin/process-resources-index.js",
|
|
16
|
+
"loir-resources-check-diff": "lib/bin/check-resources-diff.js",
|
|
15
17
|
"loir-generate-linked-data": "lib/bin/generate-linked-data.js"
|
|
16
18
|
},
|
|
17
19
|
"scripts": {
|