@larsgw/formica 0.9.0 → 0.9.1

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 CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.9.1](https://github.com/identification-resources/formica/compare/v0.9.0...v0.9.1) (2026-04-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **resources:** fix RDF mapping of taxonomic status ([c65383e](https://github.com/identification-resources/formica/commit/c65383e4d64c674c6c3b9f69e8cae925fcdf6896))
7
+
8
+
9
+ ### Features
10
+
11
+ * **catalog:** add more validation for taxa ([2bbc017](https://github.com/identification-resources/formica/commit/2bbc0177f8d4a6909339cfee812c33873770f725))
12
+
1
13
  # [0.9.0](https://github.com/identification-resources/formica/compare/v0.8.8...v0.9.0) (2026-02-04)
2
14
 
3
15
 
@@ -11,8 +23,6 @@
11
23
  * **resources:** include cluster data in DwC files ([60a28ac](https://github.com/identification-resources/formica/commit/60a28ace18b0760696735ca416a2e8d014d1c928))
12
24
  * **resources:** include uncertainty of synonymy in DwC if specified ([03b6734](https://github.com/identification-resources/formica/commit/03b6734851dc9c331ec88fab21e90ad3f0303c3c))
13
25
 
14
-
15
-
16
26
  ## [0.8.8](https://github.com/identification-resources/formica/compare/v0.8.7...v0.8.8) (2025-12-25)
17
27
 
18
28
 
@@ -20,8 +30,6 @@
20
30
 
21
31
  * **resources:** add 'subclass' rank ([4b10405](https://github.com/identification-resources/formica/commit/4b10405b32840c1675caf7ef24bf2bbfd821a236))
22
32
 
23
-
24
-
25
33
  ## [0.8.7](https://github.com/identification-resources/formica/compare/v0.8.6...v0.8.7) (2025-11-01)
26
34
 
27
35
 
@@ -29,8 +37,6 @@
29
37
 
30
38
  * **resources:** fix handling of corrections ([49c05b4](https://github.com/identification-resources/formica/commit/49c05b499262e64514b832313360d1e6a4cc6378))
31
39
 
32
-
33
-
34
40
  ## [0.8.6](https://github.com/identification-resources/formica/compare/v0.8.5...v0.8.6) (2025-10-28)
35
41
 
36
42
 
@@ -44,8 +50,6 @@
44
50
 
45
51
  * **resources:** ignore errors in later-corrected names ([4c1f69e](https://github.com/identification-resources/formica/commit/4c1f69e81450664f122cdf00e68cb918248790e6)), closes [#5](https://github.com/identification-resources/formica/issues/5)
46
52
 
47
-
48
-
49
53
  ## [0.8.5](https://github.com/identification-resources/formica/compare/v0.8.4...v0.8.5) (2025-10-28)
50
54
 
51
55
 
@@ -63,8 +67,6 @@
63
67
  * **resources:** handle subgenus synonyms of genera ([4e61336](https://github.com/identification-resources/formica/commit/4e61336e36054ffd6df57ebad5479e7ceebcb235)), closes [#16](https://github.com/identification-resources/formica/issues/16)
64
68
  * **resources:** rework parsing of resources ([cff7730](https://github.com/identification-resources/formica/commit/cff773000c5b9e3f990c619cdd45a76a18ee39a9)), closes [#15](https://github.com/identification-resources/formica/issues/15) [#7](https://github.com/identification-resources/formica/issues/7)
65
69
 
66
-
67
-
68
70
  ## [0.8.4](https://github.com/identification-resources/formica/compare/v0.8.3...v0.8.4) (2025-09-19)
69
71
 
70
72
 
@@ -78,8 +80,6 @@
78
80
 
79
81
  * **catalog:** map additional scope value ([80c6d26](https://github.com/identification-resources/formica/commit/80c6d269a7d5b03600991fb353280e81afcefb0e))
80
82
 
81
-
82
-
83
83
  ## [0.8.3](https://github.com/identification-resources/formica/compare/v0.8.2...v0.8.3) (2025-07-15)
84
84
 
85
85
 
@@ -87,8 +87,6 @@
87
87
 
88
88
  * **catalog:** add catalog field key_characteristics ([6529cfa](https://github.com/identification-resources/formica/commit/6529cfab81f759f926b81bdd9d5a36e84b526284))
89
89
 
90
-
91
-
92
90
  ## [0.8.2](https://github.com/identification-resources/formica/compare/v0.8.1...v0.8.2) (2025-06-26)
93
91
 
94
92
 
@@ -97,8 +95,6 @@
97
95
  * **resources:** add support for documented flag ([6cb89e4](https://github.com/identification-resources/formica/commit/6cb89e428bde8665b7e8f03b5c7f47a44e15f58e))
98
96
  * **resources:** allow hyphens in biname pattern ([6a58a77](https://github.com/identification-resources/formica/commit/6a58a77757b8b6ae8c2d7d01440775776d183d05))
99
97
 
100
-
101
-
102
98
  ## [0.8.1](https://github.com/identification-resources/formica/compare/v0.8.0...v0.8.1) (2025-05-30)
103
99
 
104
100
 
@@ -112,8 +108,6 @@
112
108
 
113
109
  * **resources:** add support for "(sub)gen. nov" pattern ([d2598eb](https://github.com/identification-resources/formica/commit/d2598ebfd2f80d9c2bcc00ce145b9fc229316e5b))
114
110
 
115
-
116
-
117
111
  # [0.8.0](https://github.com/identification-resources/formica/compare/v0.7.3...v0.8.0) (2025-05-07)
118
112
 
119
113
 
@@ -126,8 +120,6 @@
126
120
 
127
121
  * **catalog:** implement generation of linked data ([334616e](https://github.com/identification-resources/formica/commit/334616e2e12c0d91e49f84f06bdd00ab7a238b8e))
128
122
 
129
-
130
-
131
123
  ## [0.7.3](https://github.com/identification-resources/formica/compare/v0.7.2...v0.7.3) (2025-04-17)
132
124
 
133
125
 
@@ -135,8 +127,6 @@
135
127
 
136
128
  * **catalog:** update validation for identifiers ([156a703](https://github.com/identification-resources/formica/commit/156a7036ab09c411d091bc9d0f628831a30ba3f5))
137
129
 
138
-
139
-
140
130
  ## [0.7.2](https://github.com/identification-resources/formica/compare/v0.7.1...v0.7.2) (2025-04-02)
141
131
 
142
132
 
@@ -144,8 +134,6 @@
144
134
 
145
135
  * **catalog:** add validation for taxa.csv ([59cfcf2](https://github.com/identification-resources/formica/commit/59cfcf28ebbef42e28e648e800c178a7d528f37a))
146
136
 
147
-
148
-
149
137
  ## [0.7.1](https://github.com/identification-resources/formica/compare/v0.7.0...v0.7.1) (2025-03-25)
150
138
 
151
139
 
@@ -153,8 +141,6 @@
153
141
 
154
142
  * **resources:** add 'subphylum' rank ([8b5243b](https://github.com/identification-resources/formica/commit/8b5243bfd79b6486c613ca1178f1e1c9ec3f43a4))
155
143
 
156
-
157
-
158
144
  # [0.7.0](https://github.com/identification-resources/formica/compare/v0.6.8...v0.7.0) (2025-03-14)
159
145
 
160
146
 
@@ -172,8 +158,6 @@
172
158
 
173
159
  * **resources:** "indet" lines now have to be prefixed with "[indet]"
174
160
 
175
-
176
-
177
161
  ## [0.6.8](https://github.com/identification-resources/formica/compare/v0.6.7...v0.6.8) (2025-03-13)
178
162
 
179
163
 
@@ -183,8 +167,6 @@
183
167
  * **resources:** create index of CoL identifiers ([3792903](https://github.com/identification-resources/formica/commit/37929035374bc0c67ed2403e775e06a904bdbd4d))
184
168
  * **resources:** support intergeneric hybrids ([0a6696c](https://github.com/identification-resources/formica/commit/0a6696c5fd0603a0366c80c2298b6544636ed0c6))
185
169
 
186
-
187
-
188
170
  ## [0.6.7](https://github.com/identification-resources/formica/compare/v0.6.6...v0.6.7) (2025-02-26)
189
171
 
190
172
 
@@ -199,8 +181,6 @@
199
181
 
200
182
  * **resources:** add support for completeness flags ([55b5427](https://github.com/identification-resources/formica/commit/55b5427125b4f1072191cadbc461ba31d37e6f2e))
201
183
 
202
-
203
-
204
184
  ## [0.6.6](https://github.com/identification-resources/formica/compare/v0.6.5...v0.6.6) (2025-02-16)
205
185
 
206
186
 
@@ -208,8 +188,6 @@
208
188
 
209
189
  * **resources:** allow other resources in version_of ([85947b0](https://github.com/identification-resources/formica/commit/85947b0dcfbdb4bfd6537c628b34087a6e6326f7))
210
190
 
211
-
212
-
213
191
  ## [0.6.5](https://github.com/identification-resources/formica/compare/v0.6.4...v0.6.5) (2024-12-20)
214
192
 
215
193
 
@@ -219,8 +197,6 @@
219
197
  * **resources:** improve (ICBN) author parsing ([d3e3faf](https://github.com/identification-resources/formica/commit/d3e3faf411d1a7dccd528a5ee78bd0823a5883b4)), closes [#13](https://github.com/identification-resources/formica/issues/13) [#14](https://github.com/identification-resources/formica/issues/14)
220
198
  * **resources:** support "et al." in author name ([82993bf](https://github.com/identification-resources/formica/commit/82993bffac5e7f3ed00d00c28c836f347b264a43))
221
199
 
222
-
223
-
224
200
  ## [0.6.4](https://github.com/identification-resources/formica/compare/v0.6.3...v0.6.4) (2024-12-16)
225
201
 
226
202
 
@@ -228,8 +204,6 @@
228
204
 
229
205
  * **resources:** fix matching of some ICBN names ([3c18458](https://github.com/identification-resources/formica/commit/3c184586b64e2a31f1eb7298bcd7c80c1d119069))
230
206
 
231
-
232
-
233
207
  ## [0.6.3](https://github.com/identification-resources/formica/compare/v0.6.2...v0.6.3) (2024-04-12)
234
208
 
235
209
 
@@ -237,8 +211,6 @@
237
211
 
238
212
  * **resources:** allow 'phylum' rank ([ff9be66](https://github.com/identification-resources/formica/commit/ff9be66abab79a86263764b54d6a2709833b14d4))
239
213
 
240
-
241
-
242
214
  ## [0.6.2](https://github.com/identification-resources/formica/compare/v0.6.1...v0.6.2) (2024-04-08)
243
215
 
244
216
 
@@ -251,8 +223,6 @@
251
223
 
252
224
  * **catalog:** add script to de-duplicate links ([b719348](https://github.com/identification-resources/formica/commit/b719348635e1b35ee80758c0cdd9938625d79ebf))
253
225
 
254
-
255
-
256
226
  ## [0.6.1](https://github.com/identification-resources/formica/compare/v0.6.0...v0.6.1) (2024-03-20)
257
227
 
258
228
 
@@ -260,8 +230,6 @@
260
230
 
261
231
  * **resources:** allow two-part hybrid names ([8e3b836](https://github.com/identification-resources/formica/commit/8e3b8368985d1db35b62d306d833121150e91865))
262
232
 
263
-
264
-
265
233
  # [0.6.0](https://github.com/identification-resources/formica/compare/v0.5.2...v0.6.0) (2024-01-05)
266
234
 
267
235
 
@@ -269,8 +237,6 @@
269
237
 
270
238
  * **catalog:** add entry type, key type values ([ed6e4fb](https://github.com/identification-resources/formica/commit/ed6e4fb545e09cddb5d466f602bffb8a8483f043))
271
239
 
272
-
273
-
274
240
  ## [0.5.2](https://github.com/identification-resources/formica/compare/v0.5.1...v0.5.2) (2023-11-27)
275
241
 
276
242
 
@@ -278,8 +244,6 @@
278
244
 
279
245
  * **resources:** fix GBIF index generation ([c88e2d2](https://github.com/identification-resources/formica/commit/c88e2d2b306b930ca0b00b978e3eb89b8adfb656))
280
246
 
281
-
282
-
283
247
  ## [0.5.1](https://github.com/identification-resources/formica/compare/v0.5.0...v0.5.1) (2023-11-03)
284
248
 
285
249
 
@@ -287,8 +251,6 @@
287
251
 
288
252
  * **resources:** fix file listing in processor ([009b980](https://github.com/identification-resources/formica/commit/009b98091b0f597e91213ace82252facbe3b5fed))
289
253
 
290
-
291
-
292
254
  # [0.5.0](https://github.com/identification-resources/formica/compare/v0.4.3...v0.5.0) (2023-10-24)
293
255
 
294
256
 
@@ -319,8 +281,6 @@
319
281
  does no longer pass validation. Use the "taxon_scope" and "scope" fields
320
282
  in "catalog" instead.
321
283
 
322
-
323
-
324
284
  ## [0.4.3](https://github.com/identification-resources/formica/compare/v0.4.2...v0.4.3) (2023-09-30)
325
285
 
326
286
 
@@ -328,8 +288,6 @@ in "catalog" instead.
328
288
 
329
289
  * **resource:** fix regression in taxon parsing ([098477b](https://github.com/identification-resources/formica/commit/098477b5914324a3c15ec885617d4cf30996f30d))
330
290
 
331
-
332
-
333
291
  ## [0.4.2](https://github.com/identification-resources/formica/compare/v0.4.1...v0.4.2) (2023-09-30)
334
292
 
335
293
 
@@ -339,8 +297,6 @@ in "catalog" instead.
339
297
  * **resources:** correct synonym rank determintation ([766ea2a](https://github.com/identification-resources/formica/commit/766ea2af14ff3aa64a7a2e19ed096836d38182cf))
340
298
  * **resources:** do not parse "indet." lines as taxa ([f4debf6](https://github.com/identification-resources/formica/commit/f4debf695b0cdb375bfb60ab5ce942fa2f7dee85)), closes [#8](https://github.com/identification-resources/formica/issues/8)
341
299
 
342
-
343
-
344
300
  ## [0.4.1](https://github.com/identification-resources/formica/compare/v0.4.0...v0.4.1) (2023-09-07)
345
301
 
346
302
 
@@ -348,8 +304,6 @@ in "catalog" instead.
348
304
 
349
305
  * **catalog:** fix typo in duplicate_of schema ([fbb6333](https://github.com/identification-resources/formica/commit/fbb6333d10dd6f5846856ea8de81d941ce539d21))
350
306
 
351
-
352
-
353
307
  # [0.4.0](https://github.com/identification-resources/formica/compare/v0.3.1...v0.4.0) (2023-09-07)
354
308
 
355
309
 
@@ -363,8 +317,6 @@ in "catalog" instead.
363
317
 
364
318
  * **catalog:** add duplicate_of field ([e4a8bbe](https://github.com/identification-resources/formica/commit/e4a8bbecef9c6a40d0f2cc5d4e8bb67685a453ff))
365
319
 
366
-
367
-
368
320
  ## [0.3.1](https://github.com/identification-resources/formica/compare/v0.3.0...v0.3.1) (2023-09-04)
369
321
 
370
322
 
@@ -374,8 +326,6 @@ in "catalog" instead.
374
326
  * **resources:** fix regression in c737d5b ([d4e52e9](https://github.com/identification-resources/formica/commit/d4e52e95ed1a35dabbd80b9f9aace1a824fdae98))
375
327
  * **resources:** handle corrections to synonyms ([21ed9b7](https://github.com/identification-resources/formica/commit/21ed9b79f77e1e6d0639d96ed9822d5212a85a2e)), closes [#4](https://github.com/identification-resources/formica/issues/4)
376
328
 
377
-
378
-
379
329
  # [0.3.0](https://github.com/identification-resources/formica/compare/v0.2.1...v0.3.0) (2023-08-20)
380
330
 
381
331
 
@@ -387,8 +337,6 @@ in "catalog" instead.
387
337
  * **resources:** improve taxon name matching ([78ff480](https://github.com/identification-resources/formica/commit/78ff480485ca42bd1d1f2893f230e61db6cb6be8)), closes [#2](https://github.com/identification-resources/formica/issues/2)
388
338
  * **resources:** test for rank mismatch ([0e69b7f](https://github.com/identification-resources/formica/commit/0e69b7f0a6654fa5255e797be7516cd06edb4df3)), closes [#2](https://github.com/identification-resources/formica/issues/2)
389
339
 
390
-
391
-
392
340
  ## [0.2.1](https://github.com/identification-resources/formica/compare/v0.2.0...v0.2.1) (2023-08-10)
393
341
 
394
342
 
@@ -397,8 +345,6 @@ in "catalog" instead.
397
345
  * **resources:** do not validate old versions ([1a8dd2e](https://github.com/identification-resources/formica/commit/1a8dd2e0e373489e287d1fd89dbb7443245c5214))
398
346
  * **resources:** fix check for missing leaf taxa ([035f1ad](https://github.com/identification-resources/formica/commit/035f1ad20efa80b388ec3828d4ace4518c7b344e))
399
347
 
400
-
401
-
402
348
  # [0.2.0](https://github.com/identification-resources/formica/compare/v0.1.1...v0.2.0) (2023-08-09)
403
349
 
404
350
 
@@ -407,8 +353,6 @@ in "catalog" instead.
407
353
  * **catalog:** add taxon_scope column ([6a06c37](https://github.com/identification-resources/formica/commit/6a06c37ec0640a8ffbc258ad6a4d81d84d35fee9))
408
354
  * **resources:** check for missing leaf taxa ([387c47d](https://github.com/identification-resources/formica/commit/387c47d10bb7f1fb09a68c10dfa869bb924b7633))
409
355
 
410
-
411
-
412
356
  ## [0.1.1](https://github.com/identification-resources/formica/compare/v0.1.0...v0.1.1) (2023-05-10)
413
357
 
414
358
 
@@ -416,8 +360,6 @@ in "catalog" instead.
416
360
 
417
361
  * **processor:** add Node hashbang ([51dee57](https://github.com/identification-resources/formica/commit/51dee57b8547afba449b2b846bd7d65e7927368f))
418
362
 
419
-
420
-
421
363
  # [0.1.0](https://github.com/identification-resources/formica/compare/fed91fd6f350c47bd067d221a4d0e2278a199dae...v0.1.0) (2023-03-01)
422
364
 
423
365
 
@@ -439,6 +381,3 @@ in "catalog" instead.
439
381
  * **resources:** add support for hybrids ([e49e96a](https://github.com/identification-resources/formica/commit/e49e96af9c3958bab2a3b0508b1fc8a018b80716))
440
382
  * **validate:** add catalog validation script ([4cdf785](https://github.com/identification-resources/formica/commit/4cdf785afb2c77320cd9b5b76483e3464c0f1cff))
441
383
  * **validate:** add script to validate resources ([246fb5d](https://github.com/identification-resources/formica/commit/246fb5dd8da03e736884850874cc51a11be9985c))
442
-
443
-
444
-
@@ -125,7 +125,8 @@ const GBIF_RANKS = ['kingdom', 'phylum', 'class', 'order', 'family', 'genus', 's
125
125
  const GBIF_VOCAB_RANKS = ['domain', 'kingdom', 'subkingdom', 'superphylum', 'phylum', 'subphylum', 'superclass', 'class', 'subclass', 'supercohort', 'cohort', 'subcohort', 'superorder', 'order', 'suborder', 'infraorder', 'superfamily', 'family', 'subfamily', 'tribe', 'subtribe', 'genus', 'subgenus', 'section', 'subsection', 'series', 'subseries', 'speciesAggregate', 'species', 'subspecificAggregate', 'subspecies', 'variety', 'subvariety', 'form', 'subform', 'cultivarGroup', 'cultivar', 'strain'];
126
126
  const STATUSES = {
127
127
  'accepted': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/accepted',
128
- 'heterotypic synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/heterotypicSynonym',
128
+ 'misapplied': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/misapplied',
129
+ 'proparte synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/proParteSynonym',
129
130
  'synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/synonym',
130
131
  };
131
132
  function getCoveringTaxon(taxa) {
@@ -284,9 +285,12 @@ function makeLinkedDataForScientificName(name) {
284
285
  if (GBIF_VOCAB_RANKS.includes(name.taxonRank)) {
285
286
  node[DWC_FIELDS.taxonRank] = makeTaxonRankUri(name.taxonRank);
286
287
  }
287
- if (name.taxonomicStatus) {
288
+ if (name.taxonomicStatus in STATUSES) {
288
289
  node[DWC_FIELDS.taxonomicStatus] = makeTaxonomicStatusUri(name.taxonomicStatus);
289
290
  }
291
+ else {
292
+ console.error('Unmapped taxonomic status:', name.taxonomicStatus);
293
+ }
290
294
  if (name.acceptedNameUsageID) {
291
295
  node['dwc:acceptedNameUsageID'] = makeScientificNameUri(name.acceptedNameUsageID);
292
296
  }
@@ -46,12 +46,66 @@ Object.defineProperty(exports, "__esModule", { value: true });
46
46
  const fs_1 = require("fs");
47
47
  const path = __importStar(require("path"));
48
48
  const index_1 = require("../index");
49
+ class TaxaValidator {
50
+ constructor(taxa) {
51
+ this.taxa = Array.from(taxa).filter(taxon => taxon.has('ancestors_gbif'));
52
+ this.errors = [];
53
+ this.parentIndex = {};
54
+ }
55
+ validate() {
56
+ for (const taxon of this.taxa) {
57
+ const parents = taxon.get('ancestors_gbif');
58
+ if (parents.length > 1) {
59
+ const id = taxon.get('id');
60
+ for (let i = 1; i < parents.length; i++) {
61
+ this.addToIndex(id, 'ancestors_gbif', parents[i], parents[i - 1]);
62
+ }
63
+ }
64
+ }
65
+ for (const taxon of this.taxa) {
66
+ if (taxon.has('gbif')) {
67
+ const id = taxon.get('id');
68
+ const parents = taxon.get('ancestors_gbif');
69
+ const parent = parents[parents.length - 1];
70
+ this.addToIndex(id, 'gbif', taxon.get('gbif'), parent);
71
+ }
72
+ }
73
+ for (const taxon of this.taxa) {
74
+ if (taxon.has('children_gbif')) {
75
+ const id = taxon.get('id');
76
+ const parents = taxon.get('ancestors_gbif');
77
+ const parent = parents[parents.length - 1];
78
+ const childIds = taxon.get('children_gbif');
79
+ for (const childId of childIds) {
80
+ this.addToIndex(id, 'gbif', childId, parent);
81
+ }
82
+ }
83
+ }
84
+ return this.errors;
85
+ }
86
+ addToIndex(entity, field, childId, parentId) {
87
+ const actualParentId = this.parentIndex[childId];
88
+ if (!actualParentId) {
89
+ this.parentIndex[childId] = parentId;
90
+ }
91
+ else if (actualParentId !== parentId) {
92
+ const error = `Inconsistent ancestor of ${childId}, expected ${actualParentId} but got ${parentId}`;
93
+ this.errors.push({ entity, field, error });
94
+ }
95
+ }
96
+ }
49
97
  function validateFile(arg) {
50
98
  return __awaiter(this, void 0, void 0, function* () {
51
99
  const filePath = path.resolve(arg);
52
100
  const file = yield fs_1.promises.readFile(filePath, 'utf8');
53
101
  const sheet = path.basename(filePath, '.csv');
54
- return index_1.catalog.loadData(file, sheet).validate();
102
+ const entities = index_1.catalog.loadData(file, sheet);
103
+ const errors = entities.validate();
104
+ if (sheet === 'taxa') {
105
+ const validator = new TaxaValidator(entities);
106
+ errors.push(...validator.validate());
107
+ }
108
+ return errors;
55
109
  });
56
110
  }
57
111
  function main(args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larsgw/formica",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
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",
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "ietf-language-tag-regex": "^0.0.5",
36
36
  "js-yaml": "^4.1.0",
37
- "jsonld": "^8.3.3",
37
+ "jsonld": "^9.0.0",
38
38
  "n3": "^1.25.2",
39
39
  "spdx-license-list": "^6.6.0"
40
40
  },
@@ -106,7 +106,8 @@ const GBIF_RANKS = ['kingdom', 'phylum', 'class', 'order', 'family', 'genus', 's
106
106
  const GBIF_VOCAB_RANKS = ['domain', 'kingdom', 'subkingdom', 'superphylum', 'phylum', 'subphylum', 'superclass', 'class', 'subclass', 'supercohort', 'cohort', 'subcohort', 'superorder', 'order', 'suborder', 'infraorder', 'superfamily', 'family', 'subfamily', 'tribe', 'subtribe', 'genus', 'subgenus', 'section', 'subsection', 'series', 'subseries', 'speciesAggregate', 'species', 'subspecificAggregate', 'subspecies', 'variety', 'subvariety', 'form', 'subform', 'cultivarGroup', 'cultivar', 'strain']
107
107
  const STATUSES: Record<string, string> = {
108
108
  'accepted': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/accepted',
109
- 'heterotypic synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/heterotypicSynonym',
109
+ 'misapplied': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/misapplied',
110
+ 'proparte synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/proParteSynonym',
110
111
  'synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/synonym',
111
112
  }
112
113
 
@@ -182,8 +183,8 @@ function makeTaxonRankUri (rank: string): NodeObject|string {
182
183
  }
183
184
  }
184
185
 
185
- function makeTaxonomicStatusUri (status: string): NodeObject {
186
- return { '@id': STATUSES[status] as string }
186
+ function makeTaxonomicStatusUri (status: keyof typeof STATUSES): NodeObject {
187
+ return { '@id': STATUSES[status] }
187
188
  }
188
189
 
189
190
  function makeLinkedDataForAuthor (author: catalog.Entity): NodeObject {
@@ -313,8 +314,10 @@ function makeLinkedDataForScientificName (name: AmendedTaxon): NodeObject {
313
314
  node[DWC_FIELDS.taxonRank] = makeTaxonRankUri(name.taxonRank)
314
315
  }
315
316
 
316
- if (name.taxonomicStatus) {
317
+ if (name.taxonomicStatus in STATUSES) {
317
318
  node[DWC_FIELDS.taxonomicStatus] = makeTaxonomicStatusUri(name.taxonomicStatus)
319
+ } else {
320
+ console.error('Unmapped taxonomic status:', name.taxonomicStatus)
318
321
  }
319
322
 
320
323
  if (name.acceptedNameUsageID) {
@@ -4,11 +4,81 @@ import { promises as fs } from 'fs'
4
4
  import * as path from 'path'
5
5
  import { catalog } from '../index'
6
6
 
7
+ class TaxaValidator {
8
+ taxa: catalog.Entity[];
9
+ errors: WorkError[];
10
+ parentIndex: Record<string, string>;
11
+
12
+ constructor (taxa: catalog.Entities) {
13
+ this.taxa = Array.from(taxa).filter(taxon => taxon.has('ancestors_gbif'))
14
+ this.errors = []
15
+ this.parentIndex = {}
16
+ }
17
+
18
+ validate (): WorkError[] {
19
+ for (const taxon of this.taxa) {
20
+ const parents = taxon.get('ancestors_gbif') as string[]
21
+
22
+ if (parents.length > 1) {
23
+ const id = taxon.get('id') as string
24
+
25
+ for (let i = 1; i < parents.length; i++) {
26
+ this.addToIndex(id, 'ancestors_gbif', parents[i], parents[i - 1])
27
+ }
28
+ }
29
+ }
30
+
31
+ for (const taxon of this.taxa) {
32
+ if (taxon.has('gbif')) {
33
+ const id = taxon.get('id') as string
34
+ const parents = taxon.get('ancestors_gbif') as string[]
35
+ const parent = parents[parents.length - 1] as string
36
+
37
+ this.addToIndex(id, 'gbif', taxon.get('gbif') as string, parent)
38
+ }
39
+ }
40
+
41
+ for (const taxon of this.taxa) {
42
+ if (taxon.has('children_gbif')) {
43
+ const id = taxon.get('id') as string
44
+ const parents = taxon.get('ancestors_gbif') as string[]
45
+ const parent = parents[parents.length - 1] as string
46
+
47
+ const childIds = taxon.get('children_gbif') as string[]
48
+ for (const childId of childIds) {
49
+ this.addToIndex(id, 'gbif', childId, parent)
50
+ }
51
+ }
52
+ }
53
+
54
+ return this.errors
55
+ }
56
+
57
+ addToIndex (entity: string, field: string, childId: string, parentId: string) {
58
+ const actualParentId = this.parentIndex[childId]
59
+
60
+ if (!actualParentId) {
61
+ this.parentIndex[childId] = parentId
62
+ } else if (actualParentId !== parentId) {
63
+ const error = `Inconsistent ancestor of ${childId}, expected ${actualParentId} but got ${parentId}`
64
+ this.errors.push({ entity, field, error })
65
+ }
66
+ }
67
+ }
68
+
7
69
  async function validateFile (arg: string): Promise<WorkError[]> {
8
70
  const filePath = path.resolve(arg)
9
71
  const file = await fs.readFile(filePath, 'utf8')
10
72
  const sheet = path.basename(filePath, '.csv')
11
- return catalog.loadData(file, sheet).validate()
73
+ const entities = catalog.loadData(file, sheet)
74
+ const errors = entities.validate()
75
+
76
+ if (sheet === 'taxa') {
77
+ const validator = new TaxaValidator(entities)
78
+ errors.push(...validator.validate())
79
+ }
80
+
81
+ return errors
12
82
  }
13
83
 
14
84
  async function main (args: string[]): Promise<void> {