@larsgw/formica 0.6.6 → 0.6.7
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 +16 -0
- package/lib/bin/process-resources-problems.js +177 -0
- package/lib/resources/parse-text.js +45 -32
- package/package.json +1 -1
- package/src/bin/process-resources.ts +0 -1
- package/src/module.d.ts +3 -0
- package/src/resources/parse-text.ts +51 -37
- package/test/resources.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [0.6.7](https://github.com/identification-resources/formica/compare/v0.6.6...v0.6.7) (2025-02-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **resources:** keep uncorrected name in verbatimIdentification ([dc4d5d1](https://github.com/identification-resources/formica/commit/dc4d5d17e7a412a94538f6162dfd839d808cbc30))
|
|
7
|
+
* **resources:** update heuristics for recognizing author citations ([bca6a7f](https://github.com/identification-resources/formica/commit/bca6a7f5a6c2dc3dc421d885f6270f5464fc5ea5))
|
|
8
|
+
* **resources:** use corrected generic name in output ([b472384](https://github.com/identification-resources/formica/commit/b4723842e87515f076230c603246082bc3bbec9d))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **resources:** add support for completeness flags ([55b5427](https://github.com/identification-resources/formica/commit/55b5427125b4f1072191cadbc461ba31d37e6f2e))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
1
17
|
## [0.6.6](https://github.com/identification-resources/formica/compare/v0.6.5...v0.6.6) (2025-02-16)
|
|
2
18
|
|
|
3
19
|
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __assign = (this && this.__assign) || function () {
|
|
4
|
+
__assign = Object.assign || function(t) {
|
|
5
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
6
|
+
s = arguments[i];
|
|
7
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
8
|
+
t[p] = s[p];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
return __assign.apply(this, arguments);
|
|
13
|
+
};
|
|
14
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
15
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
16
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
17
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
18
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
19
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
20
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
24
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
25
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
26
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
27
|
+
function step(op) {
|
|
28
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
29
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
30
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
31
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
32
|
+
switch (op[0]) {
|
|
33
|
+
case 0: case 1: t = op; break;
|
|
34
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
35
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
36
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
37
|
+
default:
|
|
38
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
39
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
40
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
41
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
42
|
+
if (t[2]) _.ops.pop();
|
|
43
|
+
_.trys.pop(); continue;
|
|
44
|
+
}
|
|
45
|
+
op = body.call(thisArg, _);
|
|
46
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
47
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
var fs_1 = require("fs");
|
|
52
|
+
var path = require("path");
|
|
53
|
+
var util = require("util");
|
|
54
|
+
var index_1 = require("../index");
|
|
55
|
+
var GBIF_RANKS = [
|
|
56
|
+
'kingdom',
|
|
57
|
+
'phyllum',
|
|
58
|
+
'class',
|
|
59
|
+
'order',
|
|
60
|
+
'family',
|
|
61
|
+
'genus',
|
|
62
|
+
'species',
|
|
63
|
+
'subspecies',
|
|
64
|
+
'variety'
|
|
65
|
+
];
|
|
66
|
+
var DISPLAY_FIELDS = [
|
|
67
|
+
'scientificNameID',
|
|
68
|
+
'taxonRank',
|
|
69
|
+
'scientificName',
|
|
70
|
+
'taxonomicStatus',
|
|
71
|
+
'taxonRemarks',
|
|
72
|
+
'colTaxonID',
|
|
73
|
+
'gbifTaxonID'
|
|
74
|
+
];
|
|
75
|
+
var ResourceProblemsProcessor = /** @class */ (function () {
|
|
76
|
+
function ResourceProblemsProcessor(collectionPath) {
|
|
77
|
+
this.DIR_ROOT = path.resolve(collectionPath);
|
|
78
|
+
this.DIR_DWC = path.join(this.DIR_ROOT, 'dwc');
|
|
79
|
+
this.FILE_INDEX = path.join(this.DIR_ROOT, 'index.json');
|
|
80
|
+
this.FILE_PROBLEMS = path.join(this.DIR_ROOT, 'problems.csv');
|
|
81
|
+
}
|
|
82
|
+
ResourceProblemsProcessor.prototype.run = function () {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
84
|
+
var problems, _a, _b, resources, _c, _d, _i, problems_1, problem, _work, resourceId, _problemText, resource, missing;
|
|
85
|
+
return __generator(this, function (_e) {
|
|
86
|
+
switch (_e.label) {
|
|
87
|
+
case 0:
|
|
88
|
+
_b = (_a = index_1.csv).parseCsv;
|
|
89
|
+
return [4 /*yield*/, fs_1.promises.readFile(this.FILE_PROBLEMS, 'utf8')];
|
|
90
|
+
case 1:
|
|
91
|
+
problems = _b.apply(_a, [_e.sent()]).slice(1);
|
|
92
|
+
_d = (_c = JSON).parse;
|
|
93
|
+
return [4 /*yield*/, fs_1.promises.readFile(this.FILE_INDEX, 'utf8')];
|
|
94
|
+
case 2:
|
|
95
|
+
resources = _d.apply(_c, [_e.sent()]);
|
|
96
|
+
_i = 0, problems_1 = problems;
|
|
97
|
+
_e.label = 3;
|
|
98
|
+
case 3:
|
|
99
|
+
if (!(_i < problems_1.length)) return [3 /*break*/, 6];
|
|
100
|
+
problem = problems_1[_i];
|
|
101
|
+
_work = problem[0], resourceId = problem[1], _problemText = problem[2];
|
|
102
|
+
return [4 /*yield*/, this.readAmendedResource(resourceId, resources)];
|
|
103
|
+
case 4:
|
|
104
|
+
resource = _e.sent();
|
|
105
|
+
missing = this.getMissingMappings(resource);
|
|
106
|
+
if (missing.length === 0) {
|
|
107
|
+
this.removeProblem(resourceId);
|
|
108
|
+
}
|
|
109
|
+
_e.label = 5;
|
|
110
|
+
case 5:
|
|
111
|
+
_i++;
|
|
112
|
+
return [3 /*break*/, 3];
|
|
113
|
+
case 6: return [2 /*return*/];
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
ResourceProblemsProcessor.prototype.readAmendedResource = function (resourceId, resources) {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
120
|
+
var resource, resourceFileId, resourceFile, _a, header, taxonRows, _b, _c, taxa, _i, taxonRows_1, row, taxon;
|
|
121
|
+
return __generator(this, function (_d) {
|
|
122
|
+
switch (_d.label) {
|
|
123
|
+
case 0:
|
|
124
|
+
resource = resources[resourceId];
|
|
125
|
+
resourceFileId = resourceId.split(':').join('-');
|
|
126
|
+
resourceFile = path.join(this.DIR_DWC, "".concat(resourceFileId, ".csv"));
|
|
127
|
+
_c = (_b = index_1.csv).parseCsv;
|
|
128
|
+
return [4 /*yield*/, fs_1.promises.readFile(resourceFile, 'utf8')];
|
|
129
|
+
case 1:
|
|
130
|
+
_a = _c.apply(_b, [_d.sent()]), header = _a[0], taxonRows = _a.slice(1);
|
|
131
|
+
taxa = {};
|
|
132
|
+
for (_i = 0, taxonRows_1 = taxonRows; _i < taxonRows_1.length; _i++) {
|
|
133
|
+
row = taxonRows_1[_i];
|
|
134
|
+
taxon = row.reduce(function (taxon, value, index) {
|
|
135
|
+
taxon[header[index]] = value;
|
|
136
|
+
return taxon;
|
|
137
|
+
}, {});
|
|
138
|
+
taxa[taxon.scientificNameID] = taxon;
|
|
139
|
+
}
|
|
140
|
+
return [2 /*return*/, __assign(__assign({}, resource), { taxa: taxa })];
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
ResourceProblemsProcessor.prototype.getMissingMappings = function (resource) {
|
|
146
|
+
var missing = [];
|
|
147
|
+
for (var id in resource.taxa) {
|
|
148
|
+
var taxon = resource.taxa[id];
|
|
149
|
+
if (taxon.taxonomicStatus !== 'accepted') {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
var missingCol = false; // !taxon.colTaxonID
|
|
153
|
+
var missingGbif = GBIF_RANKS.includes(taxon.taxonRank) && !taxon.gbifTaxonID;
|
|
154
|
+
if (missingCol || missingGbif) {
|
|
155
|
+
missing.push(taxon);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return missing;
|
|
159
|
+
};
|
|
160
|
+
ResourceProblemsProcessor.prototype.removeProblem = function (resourceId) {
|
|
161
|
+
resourceId;
|
|
162
|
+
//
|
|
163
|
+
};
|
|
164
|
+
return ResourceProblemsProcessor;
|
|
165
|
+
}());
|
|
166
|
+
function main() {
|
|
167
|
+
var args = util.parseArgs({ allowPositionals: true });
|
|
168
|
+
var processor = new ResourceProblemsProcessor(args.positionals[0]);
|
|
169
|
+
process.on('exit', function () {
|
|
170
|
+
process.stdout.write('\n');
|
|
171
|
+
});
|
|
172
|
+
processor.run().catch(function (error) {
|
|
173
|
+
console.error(error);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
main();
|
|
@@ -64,6 +64,11 @@ var DWC_RANKS = [
|
|
|
64
64
|
'genus',
|
|
65
65
|
'subgenus'
|
|
66
66
|
];
|
|
67
|
+
var FLAGS = [
|
|
68
|
+
'MISSING_TAXA',
|
|
69
|
+
'MISSING_PARENT_TAXA',
|
|
70
|
+
'MISSING_SYNONYMS'
|
|
71
|
+
];
|
|
67
72
|
var TAXONOMIC_STATUS = {
|
|
68
73
|
'>': 'incorrect',
|
|
69
74
|
'+': 'heterotypic synonym',
|
|
@@ -103,7 +108,7 @@ var RANK_LABELS_REVERSE = {
|
|
|
103
108
|
* 2. Followed by a capitalized word
|
|
104
109
|
* 3. Optionally, followed by "et al."
|
|
105
110
|
*/
|
|
106
|
-
var LOWERCASE_NAME_PARTICLES = ['y', 'der', 'den', 'de', 'van'].join('|');
|
|
111
|
+
var LOWERCASE_NAME_PARTICLES = ['y', 'der', 'den', 'de', 'van', 'von'].join('|');
|
|
107
112
|
var SIMPLE_AUTHOR_PATTERN = '(?:(?:\\p{Lu}\\S*|&|in|ex|' + LOWERCASE_NAME_PARTICLES + ')\\s*)*\\p{Lu}\\S+(?:\\s+et\\s+al\\.)?';
|
|
108
113
|
var NAME_PATTERN = new RegExp('^' +
|
|
109
114
|
// $1 main name part
|
|
@@ -111,7 +116,7 @@ var NAME_PATTERN = new RegExp('^' +
|
|
|
111
116
|
// $2 optional author citation
|
|
112
117
|
'(?: ' +
|
|
113
118
|
// but not auct(t)., etc.
|
|
114
|
-
'(?!auctt?\\.|(?:syn|comb|sp|spec)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.))' +
|
|
119
|
+
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
115
120
|
'(' +
|
|
116
121
|
// $2.1 anything in parentheses, followed by optional revising author(s)
|
|
117
122
|
'\\(.+?\\)(?:\\s+' + SIMPLE_AUTHOR_PATTERN + ')?' +
|
|
@@ -326,17 +331,6 @@ function parseName(name, rank, parent) {
|
|
|
326
331
|
if (item.scientificNameAuthorship) {
|
|
327
332
|
item.scientificName += " ".concat(item.scientificNameAuthorship);
|
|
328
333
|
}
|
|
329
|
-
// Amend "parent" with corrections
|
|
330
|
-
if (item.taxonomicStatus === 'incorrect') {
|
|
331
|
-
var itemAsObject = item;
|
|
332
|
-
var parentAsObject = parent;
|
|
333
|
-
parent.incorrect = __assign({}, parent);
|
|
334
|
-
for (var key in item) {
|
|
335
|
-
if (key !== 'taxonomicStatus') {
|
|
336
|
-
parentAsObject[key] = itemAsObject[key];
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
334
|
return item;
|
|
341
335
|
}
|
|
342
336
|
function parseHeader(header) {
|
|
@@ -401,6 +395,16 @@ function parseHeader(header) {
|
|
|
401
395
|
metadata.catalog[key] = work.get(key);
|
|
402
396
|
}
|
|
403
397
|
}
|
|
398
|
+
if ('flags' in config) {
|
|
399
|
+
if (!Array.isArray(config.flags)) {
|
|
400
|
+
throw new SyntaxError('"flags" should be an array if present');
|
|
401
|
+
}
|
|
402
|
+
var invalidFlags = config.flags.filter(function (flag) { return !FLAGS.includes(flag); });
|
|
403
|
+
if (invalidFlags.length) {
|
|
404
|
+
throw new SyntaxError("\"flags\" contains invalid values: ".concat(invalidFlags.join(', ')));
|
|
405
|
+
}
|
|
406
|
+
metadata.flags = config.flags;
|
|
407
|
+
}
|
|
404
408
|
return metadata;
|
|
405
409
|
}
|
|
406
410
|
function validateResource(config, content) {
|
|
@@ -480,27 +484,11 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
480
484
|
var rank = resource.metadata.levels[groupIndent / 2];
|
|
481
485
|
var item = parseName(name_1, rank, parent_1);
|
|
482
486
|
var isSynonym = item.taxonomicStatus !== 'accepted';
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if (type === diff_resource_1.ResourceDiffType.Added) {
|
|
487
|
-
newIdOffset++;
|
|
488
|
-
item.scientificNameID = idBase + newIdOffset.toString();
|
|
489
|
-
}
|
|
490
|
-
else {
|
|
491
|
-
id++;
|
|
492
|
-
item.scientificNameID = idBase + (oldIds[id - 1] || id).toString();
|
|
493
|
-
}
|
|
494
|
-
previousId = item.scientificNameID;
|
|
495
|
-
item.parentNameUsageID = isSynonym ? undefined : parent_1.scientificNameID;
|
|
496
|
-
item.parentNameUsage = isSynonym ? undefined : parent_1.scientificName;
|
|
497
|
-
item.acceptedNameUsageID = isSynonym ? parent_1.scientificNameID : undefined;
|
|
498
|
-
item.acceptedNameUsage = isSynonym ? parent_1.scientificName : undefined;
|
|
499
|
-
item.collectionCode = idBase.slice(0, -1);
|
|
487
|
+
// Add higher classification info
|
|
488
|
+
var itemAsObject = item;
|
|
489
|
+
var parentAsObject = parent_1;
|
|
500
490
|
for (var _b = 0, DWC_RANKS_1 = DWC_RANKS; _b < DWC_RANKS_1.length; _b++) {
|
|
501
491
|
var rank_1 = DWC_RANKS_1[_b];
|
|
502
|
-
var itemAsObject = item;
|
|
503
|
-
var parentAsObject = parent_1;
|
|
504
492
|
itemAsObject[rank_1] = undefined;
|
|
505
493
|
if (parentAsObject[rank_1]) {
|
|
506
494
|
itemAsObject[rank_1] = parentAsObject[rank_1];
|
|
@@ -524,6 +512,31 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
524
512
|
else if (parentId) {
|
|
525
513
|
item.higherClassification = parent_1.scientificNameOnly;
|
|
526
514
|
}
|
|
515
|
+
// Amend "parent" with corrections
|
|
516
|
+
if (item.taxonomicStatus === 'incorrect') {
|
|
517
|
+
parent_1.incorrect = __assign({}, parent_1);
|
|
518
|
+
for (var key in item) {
|
|
519
|
+
if (key !== 'taxonomicStatus' && key !== 'verbatimIdentification') {
|
|
520
|
+
parentAsObject[key] = itemAsObject[key];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return "continue";
|
|
524
|
+
}
|
|
525
|
+
// Set identifiers
|
|
526
|
+
if (type === diff_resource_1.ResourceDiffType.Added) {
|
|
527
|
+
newIdOffset++;
|
|
528
|
+
item.scientificNameID = idBase + newIdOffset.toString();
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
id++;
|
|
532
|
+
item.scientificNameID = idBase + (oldIds[id - 1] || id).toString();
|
|
533
|
+
}
|
|
534
|
+
previousId = item.scientificNameID;
|
|
535
|
+
item.parentNameUsageID = isSynonym ? undefined : parent_1.scientificNameID;
|
|
536
|
+
item.parentNameUsage = isSynonym ? undefined : parent_1.scientificName;
|
|
537
|
+
item.acceptedNameUsageID = isSynonym ? parent_1.scientificNameID : undefined;
|
|
538
|
+
item.acceptedNameUsage = isSynonym ? parent_1.scientificName : undefined;
|
|
539
|
+
item.collectionCode = idBase.slice(0, -1);
|
|
527
540
|
data[item.scientificNameID] = item;
|
|
528
541
|
};
|
|
529
542
|
for (var _i = 0, content_1 = content; _i < content_1.length; _i++) {
|
package/package.json
CHANGED
|
@@ -175,7 +175,6 @@ class ResourceProcessor {
|
|
|
175
175
|
const skip = await this.shouldBeSkipped(resource.id)
|
|
176
176
|
|
|
177
177
|
if (!skip) {
|
|
178
|
-
// TODO const correct = checkResults(results, classifications)
|
|
179
178
|
const correct = this.checkResults(results)
|
|
180
179
|
if (!correct) {
|
|
181
180
|
const choice = await promptForAnswers(
|
package/src/module.d.ts
CHANGED
|
@@ -54,6 +54,12 @@ const DWC_RANKS: DwcRank[] = [
|
|
|
54
54
|
'subgenus'
|
|
55
55
|
]
|
|
56
56
|
|
|
57
|
+
const FLAGS: ResourceFlag[] = [
|
|
58
|
+
'MISSING_TAXA',
|
|
59
|
+
'MISSING_PARENT_TAXA',
|
|
60
|
+
'MISSING_SYNONYMS'
|
|
61
|
+
]
|
|
62
|
+
|
|
57
63
|
const TAXONOMIC_STATUS: Record<string, TaxonStatus> = {
|
|
58
64
|
'>': 'incorrect',
|
|
59
65
|
'+': 'heterotypic synonym',
|
|
@@ -97,7 +103,7 @@ const RANK_LABELS_REVERSE: Record<string, Rank> = {
|
|
|
97
103
|
* 2. Followed by a capitalized word
|
|
98
104
|
* 3. Optionally, followed by "et al."
|
|
99
105
|
*/
|
|
100
|
-
const LOWERCASE_NAME_PARTICLES = ['y', 'der', 'den', 'de', 'van'].join('|')
|
|
106
|
+
const LOWERCASE_NAME_PARTICLES = ['y', 'der', 'den', 'de', 'van', 'von'].join('|')
|
|
101
107
|
const SIMPLE_AUTHOR_PATTERN = '(?:(?:\\p{Lu}\\S*|&|in|ex|' + LOWERCASE_NAME_PARTICLES + ')\\s*)*\\p{Lu}\\S+(?:\\s+et\\s+al\\.)?'
|
|
102
108
|
|
|
103
109
|
const NAME_PATTERN = new RegExp(
|
|
@@ -107,7 +113,7 @@ const NAME_PATTERN = new RegExp(
|
|
|
107
113
|
// $2 optional author citation
|
|
108
114
|
'(?: ' +
|
|
109
115
|
// but not auct(t)., etc.
|
|
110
|
-
'(?!auctt?\\.|(?:syn|comb|sp|spec)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.))' +
|
|
116
|
+
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
111
117
|
'(' +
|
|
112
118
|
// $2.1 anything in parentheses, followed by optional revising author(s)
|
|
113
119
|
'\\(.+?\\)(?:\\s+' + SIMPLE_AUTHOR_PATTERN + ')?' +
|
|
@@ -342,19 +348,6 @@ function parseName (name: string, rank: Rank, parent: WorkingTaxon): WorkingTaxo
|
|
|
342
348
|
item.scientificName += ` ${item.scientificNameAuthorship}`
|
|
343
349
|
}
|
|
344
350
|
|
|
345
|
-
// Amend "parent" with corrections
|
|
346
|
-
if (item.taxonomicStatus === 'incorrect') {
|
|
347
|
-
const itemAsObject = item as { [index: string]: unknown }
|
|
348
|
-
const parentAsObject = parent as { [index: string]: unknown }
|
|
349
|
-
|
|
350
|
-
parent.incorrect = { ...parent }
|
|
351
|
-
for (const key in item) {
|
|
352
|
-
if (key !== 'taxonomicStatus') {
|
|
353
|
-
parentAsObject[key] = itemAsObject[key]
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
351
|
return item
|
|
359
352
|
}
|
|
360
353
|
|
|
@@ -418,6 +411,19 @@ function parseHeader (header: string): ResourceMetadata {
|
|
|
418
411
|
}
|
|
419
412
|
}
|
|
420
413
|
|
|
414
|
+
if ('flags' in config) {
|
|
415
|
+
if (!Array.isArray(config.flags)) {
|
|
416
|
+
throw new SyntaxError('"flags" should be an array if present')
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const invalidFlags = config.flags.filter(flag => !FLAGS.includes(flag))
|
|
420
|
+
if (invalidFlags.length) {
|
|
421
|
+
throw new SyntaxError(`"flags" contains invalid values: ${invalidFlags.join(', ')}`)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
metadata.flags = config.flags
|
|
425
|
+
}
|
|
426
|
+
|
|
421
427
|
return metadata
|
|
422
428
|
}
|
|
423
429
|
|
|
@@ -510,29 +516,10 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
510
516
|
const item = parseName(name, rank, parent)
|
|
511
517
|
const isSynonym = item.taxonomicStatus !== 'accepted'
|
|
512
518
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (type === ResourceDiffType.Added) {
|
|
518
|
-
newIdOffset++
|
|
519
|
-
item.scientificNameID = idBase + newIdOffset.toString()
|
|
520
|
-
} else {
|
|
521
|
-
id++
|
|
522
|
-
item.scientificNameID = idBase + (oldIds[id - 1] || id).toString()
|
|
523
|
-
}
|
|
524
|
-
previousId = item.scientificNameID
|
|
525
|
-
|
|
526
|
-
item.parentNameUsageID = isSynonym ? undefined : parent.scientificNameID
|
|
527
|
-
item.parentNameUsage = isSynonym ? undefined : parent.scientificName
|
|
528
|
-
item.acceptedNameUsageID = isSynonym ? parent.scientificNameID : undefined
|
|
529
|
-
item.acceptedNameUsage = isSynonym ? parent.scientificName : undefined
|
|
530
|
-
item.collectionCode = idBase.slice(0, -1)
|
|
531
|
-
|
|
519
|
+
// Add higher classification info
|
|
520
|
+
const itemAsObject = item as { [index: string]: unknown }
|
|
521
|
+
const parentAsObject = parent as { [index: string]: unknown }
|
|
532
522
|
for (const rank of DWC_RANKS) {
|
|
533
|
-
const itemAsObject = item as { [index: string]: unknown }
|
|
534
|
-
const parentAsObject = parent as { [index: string]: unknown }
|
|
535
|
-
|
|
536
523
|
itemAsObject[rank] = undefined
|
|
537
524
|
if (parentAsObject[rank]) {
|
|
538
525
|
itemAsObject[rank] = parentAsObject[rank]
|
|
@@ -557,6 +544,33 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
557
544
|
item.higherClassification = parent.scientificNameOnly
|
|
558
545
|
}
|
|
559
546
|
|
|
547
|
+
// Amend "parent" with corrections
|
|
548
|
+
if (item.taxonomicStatus === 'incorrect') {
|
|
549
|
+
parent.incorrect = { ...parent }
|
|
550
|
+
for (const key in item) {
|
|
551
|
+
if (key !== 'taxonomicStatus' && key !== 'verbatimIdentification') {
|
|
552
|
+
parentAsObject[key] = itemAsObject[key]
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
continue
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Set identifiers
|
|
559
|
+
if (type === ResourceDiffType.Added) {
|
|
560
|
+
newIdOffset++
|
|
561
|
+
item.scientificNameID = idBase + newIdOffset.toString()
|
|
562
|
+
} else {
|
|
563
|
+
id++
|
|
564
|
+
item.scientificNameID = idBase + (oldIds[id - 1] || id).toString()
|
|
565
|
+
}
|
|
566
|
+
previousId = item.scientificNameID
|
|
567
|
+
|
|
568
|
+
item.parentNameUsageID = isSynonym ? undefined : parent.scientificNameID
|
|
569
|
+
item.parentNameUsage = isSynonym ? undefined : parent.scientificName
|
|
570
|
+
item.acceptedNameUsageID = isSynonym ? parent.scientificNameID : undefined
|
|
571
|
+
item.acceptedNameUsage = isSynonym ? parent.scientificName : undefined
|
|
572
|
+
item.collectionCode = idBase.slice(0, -1)
|
|
573
|
+
|
|
560
574
|
data[item.scientificNameID] = item
|
|
561
575
|
}
|
|
562
576
|
|
package/test/resources.js
CHANGED
|
@@ -136,4 +136,17 @@ Tilia x vulgaris
|
|
|
136
136
|
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Rumex conglomeratus×maritimus')
|
|
137
137
|
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Tilia ×vulgaris')
|
|
138
138
|
})
|
|
139
|
+
|
|
140
|
+
await t.test('outputs corrected generic names', (t) => {
|
|
141
|
+
const [resource] = resources.parseTextFile(`---
|
|
142
|
+
levels: [genus, species]
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
Bogdania Kerzhner, 1964
|
|
146
|
+
> Bogdiana Kerzhner, 1964
|
|
147
|
+
myrmica Kerzhner, 1964
|
|
148
|
+
`, 'T1')
|
|
149
|
+
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Bogdiana Kerzhner, 1964')
|
|
150
|
+
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Bogdiana myrmica Kerzhner, 1964')
|
|
151
|
+
})
|
|
139
152
|
})
|