@larsgw/formica 0.8.0 → 0.8.2
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 +25 -0
- package/lib/bin/generate-linked-data.js +56 -12
- package/lib/bin/validate-linked-data.js +0 -0
- package/lib/catalog/tables/author.js +1 -1
- package/lib/catalog/tables/place.js +1 -1
- package/lib/catalog/tables/publisher.js +1 -1
- package/lib/resources/parse-text.js +8 -7
- package/package.json +1 -1
- package/src/bin/generate-linked-data.ts +58 -12
- package/src/catalog/tables/author.ts +1 -1
- package/src/catalog/tables/place.ts +1 -1
- package/src/catalog/tables/publisher.ts +1 -1
- package/src/resources/parse-text.ts +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## [0.8.2](https://github.com/identification-resources/formica/compare/v0.8.1...v0.8.2) (2025-06-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **resources:** add support for documented flag ([6cb89e4](https://github.com/identification-resources/formica/commit/6cb89e428bde8665b7e8f03b5c7f47a44e15f58e))
|
|
7
|
+
* **resources:** allow hyphens in biname pattern ([6a58a77](https://github.com/identification-resources/formica/commit/6a58a77757b8b6ae8c2d7d01440775776d183d05))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [0.8.1](https://github.com/identification-resources/formica/compare/v0.8.0...v0.8.1) (2025-05-30)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **catalog:** fix aspects of linked data ([1b2964f](https://github.com/identification-resources/formica/commit/1b2964f46b8409c6a67f0475faf067a80734288c))
|
|
17
|
+
* **catalog:** set display_name to required, as documented ([3f36909](https://github.com/identification-resources/formica/commit/3f36909bbe6261cbee022139050875de37174611))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **resources:** add support for "(sub)gen. nov" pattern ([d2598eb](https://github.com/identification-resources/formica/commit/d2598ebfd2f80d9c2bcc00ce145b9fc229316e5b))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
1
26
|
# [0.8.0](https://github.com/identification-resources/formica/compare/v0.7.3...v0.8.0) (2025-05-07)
|
|
2
27
|
|
|
3
28
|
|
|
@@ -129,7 +129,6 @@ var SCOPES = {
|
|
|
129
129
|
'queens': ['dwc:caste', 'queen'],
|
|
130
130
|
'workers': ['dwc:caste', 'worker'],
|
|
131
131
|
'soldiers': ['dwc:caste', 'soldier'],
|
|
132
|
-
'alates': ['dwc:caste', 'alate'],
|
|
133
132
|
'alatae': ['dwc:caste', 'alate'],
|
|
134
133
|
'apterae': ['dwc:caste', 'aptera'],
|
|
135
134
|
'viviparae': ['dwc:caste', 'vivipara'],
|
|
@@ -151,7 +150,9 @@ var PREFIXES = {
|
|
|
151
150
|
'dwciri': 'http://rs.tdwg.org/dwc/iri/',
|
|
152
151
|
'foaf': 'http://xmlns.com/foaf/0.1/',
|
|
153
152
|
'owl': 'http://www.w3.org/2002/07/owl#',
|
|
153
|
+
'rdfs': 'http://www.w3.org/2000/01/rdf-schema#',
|
|
154
154
|
'schema': 'https://schema.org/',
|
|
155
|
+
'xsd': 'http://www.w3.org/2001/XMLSchema#',
|
|
155
156
|
};
|
|
156
157
|
var DWC_FIELDS = {
|
|
157
158
|
scientificName: 'dwc:scientificName',
|
|
@@ -166,6 +167,12 @@ var DWC_FIELDS = {
|
|
|
166
167
|
verbatimIdentification: 'dwc:verbatimIdentification',
|
|
167
168
|
};
|
|
168
169
|
var GBIF_RANKS = ['kingdom', 'phylum', 'class', 'order', 'family', 'genus', 'species', 'subspecies'];
|
|
170
|
+
var 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'];
|
|
171
|
+
var STATUSES = {
|
|
172
|
+
'accepted': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/accepted',
|
|
173
|
+
'heterotypic synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/heterotypicSynonym',
|
|
174
|
+
'synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/synonym',
|
|
175
|
+
};
|
|
169
176
|
function getCoveringTaxon(taxa) {
|
|
170
177
|
if (taxa.length === 1 && !taxa[0].has('gbif')) {
|
|
171
178
|
return "".concat(PREFIX, "taxon/").concat(taxa[0].get('id'));
|
|
@@ -222,6 +229,17 @@ function makeScientificNameUri(id) {
|
|
|
222
229
|
var resource = id.split(':').slice(0, -1).join(':');
|
|
223
230
|
return { '@id': "".concat(PREFIX, "resource/").concat(resource, "#").concat(id) };
|
|
224
231
|
}
|
|
232
|
+
function makeTaxonRankUri(rank) {
|
|
233
|
+
if (GBIF_VOCAB_RANKS.includes(rank)) {
|
|
234
|
+
return { '@id': "http://rs.gbif.org/vocabulary/gbif/rank/".concat(rank) };
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
return rank;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function makeTaxonomicStatusUri(status) {
|
|
241
|
+
return { '@id': STATUSES[status] };
|
|
242
|
+
}
|
|
225
243
|
function makeLinkedDataForAuthor(author) {
|
|
226
244
|
var node = {
|
|
227
245
|
'@id': "".concat(PREFIX, "author/").concat(author.get('id')),
|
|
@@ -246,7 +264,7 @@ function makeLinkedDataForPlace(place) {
|
|
|
246
264
|
}
|
|
247
265
|
function makeLinkedDataForPublisher(publisher) {
|
|
248
266
|
var node = {
|
|
249
|
-
'@id': "".concat(PREFIX, "
|
|
267
|
+
'@id': "".concat(PREFIX, "publisher/").concat(publisher.get('id')),
|
|
250
268
|
'@type': 'foaf:Organization',
|
|
251
269
|
'foaf:name': publisher.get('display_name')
|
|
252
270
|
};
|
|
@@ -262,7 +280,7 @@ function makeLinkedDataForTaxon(taxon) {
|
|
|
262
280
|
'dwc:scientificName': taxon.get('display_name'),
|
|
263
281
|
};
|
|
264
282
|
if (taxon.has('rank')) {
|
|
265
|
-
node['dwc:taxonRank'] = taxon.get('rank');
|
|
283
|
+
node['dwc:taxonRank'] = makeTaxonRankUri(taxon.get('rank'));
|
|
266
284
|
}
|
|
267
285
|
var ids = [];
|
|
268
286
|
if (taxon.has('qid')) {
|
|
@@ -291,7 +309,7 @@ function makeLinkedDataForTaxa(files) {
|
|
|
291
309
|
node['dwc:parentNameUsageID'] = { '@id': "".concat(PREFIX, "taxon/T141") };
|
|
292
310
|
}
|
|
293
311
|
for (var i = 0; i < ancestors.length; i++) {
|
|
294
|
-
gbifTaxa[ancestors[i]] = __assign(__assign({}, makeGbifUri(ancestors[i])), { 'dwc:taxonRank': GBIF_RANKS[i], 'dwc:parentNameUsageID': i ? makeGbifUri(ancestors[i - 1]) : { '@id': "".concat(PREFIX, "taxon/T141") } });
|
|
312
|
+
gbifTaxa[ancestors[i]] = __assign(__assign({}, makeGbifUri(ancestors[i])), { 'dwc:taxonRank': makeTaxonRankUri(GBIF_RANKS[i]), 'dwc:parentNameUsageID': i ? makeGbifUri(ancestors[i - 1]) : { '@id': "".concat(PREFIX, "taxon/T141") } });
|
|
295
313
|
}
|
|
296
314
|
if (taxon.has('children_gbif')) {
|
|
297
315
|
var children = taxon.get('children_gbif');
|
|
@@ -299,7 +317,7 @@ function makeLinkedDataForTaxa(files) {
|
|
|
299
317
|
var childRank = GBIF_RANKS[ancestors.length];
|
|
300
318
|
for (var _c = 0, children_1 = children; _c < children_1.length; _c++) {
|
|
301
319
|
var child = children_1[_c];
|
|
302
|
-
gbifTaxa[child] = __assign(__assign({}, makeGbifUri(child)), { 'dwc:taxonRank': childRank });
|
|
320
|
+
gbifTaxa[child] = __assign(__assign({}, makeGbifUri(child)), { 'dwc:taxonRank': makeTaxonRankUri(childRank) });
|
|
303
321
|
}
|
|
304
322
|
}
|
|
305
323
|
nodes.push(node);
|
|
@@ -315,6 +333,12 @@ function makeLinkedDataForScientificName(name) {
|
|
|
315
333
|
node[DWC_FIELDS[field]] = value;
|
|
316
334
|
}
|
|
317
335
|
}
|
|
336
|
+
if (GBIF_VOCAB_RANKS.includes(name.taxonRank)) {
|
|
337
|
+
node[DWC_FIELDS.taxonRank] = makeTaxonRankUri(name.taxonRank);
|
|
338
|
+
}
|
|
339
|
+
if (name.taxonomicStatus) {
|
|
340
|
+
node[DWC_FIELDS.taxonomicStatus] = makeTaxonomicStatusUri(name.taxonomicStatus);
|
|
341
|
+
}
|
|
318
342
|
if (name.acceptedNameUsageID) {
|
|
319
343
|
node['dwc:acceptedNameUsageID'] = makeScientificNameUri(name.acceptedNameUsageID);
|
|
320
344
|
}
|
|
@@ -436,7 +460,10 @@ function makeLinkedDataForWork(work, files) {
|
|
|
436
460
|
node['dcterms:language'] = languages.map(function (language) { return ({ '@id': "http://id.loc.gov/vocabulary/iso639-1/".concat(language) }); });
|
|
437
461
|
if (work.has('title')) {
|
|
438
462
|
var title = work.get('title');
|
|
439
|
-
|
|
463
|
+
if (title.length > languages.length) {
|
|
464
|
+
title.splice(0, title.length, title.join('; '));
|
|
465
|
+
}
|
|
466
|
+
node['dcterms:title'] = title.map(function (value, i) { return ({ '@value': value, '@language': languages[i] }); });
|
|
440
467
|
}
|
|
441
468
|
if (work.has('pages') && work.get('pages').includes('-')) {
|
|
442
469
|
var containers = work.has('part_of') ? work.get('part_of').map(function (id) { return files.catalog.get(id); }) : [];
|
|
@@ -462,7 +489,7 @@ function makeLinkedDataForWork(work, files) {
|
|
|
462
489
|
var urls = work.get('url');
|
|
463
490
|
var handle = urls.find(function (url) { return url.startsWith(HANDLE_PREFIX); });
|
|
464
491
|
if (handle) {
|
|
465
|
-
node['bibo:handle'] = handle.slice(HANDLE_PREFIX.length);
|
|
492
|
+
node['bibo:handle'] = { '@id': handle.slice(HANDLE_PREFIX.length) };
|
|
466
493
|
}
|
|
467
494
|
node['schema:url'] = urls.map(function (url) { return ({ '@id': url }); });
|
|
468
495
|
}
|
|
@@ -474,7 +501,18 @@ function makeLinkedDataForWork(work, files) {
|
|
|
474
501
|
node['schema:archivedAt'] = work.get('archive_url').map(function (url) { return ({ '@id': url }); });
|
|
475
502
|
}
|
|
476
503
|
if (work.has('date')) {
|
|
477
|
-
|
|
504
|
+
var date = work.get('date');
|
|
505
|
+
var dateType = 'rdfs:Literal';
|
|
506
|
+
if (date.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
507
|
+
dateType = 'xsd:date';
|
|
508
|
+
}
|
|
509
|
+
else if (date.match(/^\d{4}-\d{2}$/)) {
|
|
510
|
+
dateType = 'xsd:gYearMonth';
|
|
511
|
+
}
|
|
512
|
+
else if (date.match(/^\d{4}$/)) {
|
|
513
|
+
dateType = 'xsd:gYear';
|
|
514
|
+
}
|
|
515
|
+
node['dcterms:issued'] = { '@value': work.get('date'), '@type': dateType };
|
|
478
516
|
}
|
|
479
517
|
if (work.has('publisher')) {
|
|
480
518
|
var publishers = mapEntities(work.get('publisher'), files.publishers.entities).map(function (publisher) { return ({ '@id': "".concat(PREFIX, "publisher/").concat(publisher.get('id')) }); });
|
|
@@ -519,9 +557,15 @@ function makeLinkedDataForWork(work, files) {
|
|
|
519
557
|
else if (range.length === 2) {
|
|
520
558
|
node['bibo:pages'] = pages;
|
|
521
559
|
var _a = range.map(function (part) { return parseInt(part); }), start = _a[0], end = _a[1];
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
560
|
+
if (!isNaN(start)) {
|
|
561
|
+
node['bibo:pageStart'] = start;
|
|
562
|
+
}
|
|
563
|
+
if (!isNaN(end)) {
|
|
564
|
+
node['bibo:pageEnd'] = end;
|
|
565
|
+
}
|
|
566
|
+
if (!isNaN(start) && !isNaN(end)) {
|
|
567
|
+
node['bibo:numPages'] = end - start + 1;
|
|
568
|
+
}
|
|
525
569
|
}
|
|
526
570
|
else {
|
|
527
571
|
node['bibo:pages'] = pages;
|
|
@@ -582,7 +626,7 @@ function makeLinkedDataForWorks(files) {
|
|
|
582
626
|
var originals = work.get('version_of');
|
|
583
627
|
var originalLanguages_1 = originals.map(function (id) { return works.get(id).get('language').join(); });
|
|
584
628
|
node['bibo:translationOf'] = originals.filter(function (_, i) { return originalLanguages_1[i] !== language.join(); }).map(makeWorkUri);
|
|
585
|
-
node['dcterms:isVersionOf'] = originals.map(makeWorkUri);
|
|
629
|
+
node['dcterms:isVersionOf'] = originals.filter(function (id) { return work.get('id') !== id; }).map(makeWorkUri);
|
|
586
630
|
}
|
|
587
631
|
var resourceIndex = 1;
|
|
588
632
|
var resourceId = void 0;
|
|
File without changes
|
|
@@ -25,7 +25,7 @@ var Author = /** @class */ (function (_super) {
|
|
|
25
25
|
name: { required: true, multiple: true },
|
|
26
26
|
id: { required: true, multiple: false, format: value_1.FORMATS.AUTHOR_ID },
|
|
27
27
|
qid: { required: false, multiple: false, format: value_1.FORMATS.QID },
|
|
28
|
-
display_name: { required:
|
|
28
|
+
display_name: { required: true, multiple: false },
|
|
29
29
|
full_names: { required: false, multiple: true },
|
|
30
30
|
duplicate_of: { required: false, multiple: false, format: value_1.FORMATS.AUTHOR_ID }
|
|
31
31
|
}) || this;
|
|
@@ -25,7 +25,7 @@ var Place = /** @class */ (function (_super) {
|
|
|
25
25
|
name: { required: true, multiple: false },
|
|
26
26
|
id: { required: true, multiple: false, format: value_1.FORMATS.PLACE_ID },
|
|
27
27
|
qid: { required: false, multiple: false, format: value_1.FORMATS.QID },
|
|
28
|
-
display_name: { required:
|
|
28
|
+
display_name: { required: true, multiple: false },
|
|
29
29
|
duplicate_of: { required: false, multiple: false, format: value_1.FORMATS.PLACE_ID }
|
|
30
30
|
}) || this;
|
|
31
31
|
}
|
|
@@ -25,7 +25,7 @@ var Publisher = /** @class */ (function (_super) {
|
|
|
25
25
|
name: { required: true, multiple: true },
|
|
26
26
|
id: { required: true, multiple: false, format: value_1.FORMATS.PUBLISHER_ID },
|
|
27
27
|
qid: { required: false, multiple: false, format: value_1.FORMATS.QID },
|
|
28
|
-
display_name: { required:
|
|
28
|
+
display_name: { required: true, multiple: false },
|
|
29
29
|
full_names: { required: false, multiple: true },
|
|
30
30
|
duplicate_of: { required: false, multiple: false, format: value_1.FORMATS.PUBLISHER_ID }
|
|
31
31
|
}) || this;
|
|
@@ -102,7 +102,8 @@ var DWC_RANKS = [
|
|
|
102
102
|
var FLAGS = [
|
|
103
103
|
'MISSING_TAXA',
|
|
104
104
|
'MISSING_PARENT_TAXA',
|
|
105
|
-
'MISSING_SYNONYMS'
|
|
105
|
+
'MISSING_SYNONYMS',
|
|
106
|
+
'MISSING_AUTHORSHIP'
|
|
106
107
|
];
|
|
107
108
|
var TAXONOMIC_STATUS = {
|
|
108
109
|
'>': 'incorrect',
|
|
@@ -145,7 +146,7 @@ var NAME_PATTERN = new RegExp('^' +
|
|
|
145
146
|
// $2 optional author citation
|
|
146
147
|
'(?: ' +
|
|
147
148
|
// but not auct(t)., etc.
|
|
148
|
-
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
149
|
+
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom|gen|subgen)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
149
150
|
'(' +
|
|
150
151
|
// $2.1 anything in parentheses, followed by optional revising author(s)
|
|
151
152
|
'\\(.+?\\)(?:\\s+' + SIMPLE_AUTHOR_PATTERN + ')?' +
|
|
@@ -164,12 +165,12 @@ var NAME_PATTERN = new RegExp('^' +
|
|
|
164
165
|
* $1 genus+subgenus (+ trailing space): (?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?
|
|
165
166
|
* $1.1 genus: ((?:x )?[A-Z]\S+)
|
|
166
167
|
* $1.2 subgenus: (?:\(([A-Z]\S+?)\) )?
|
|
167
|
-
* $2 species: (x [a-z]+|[a-z][^\s.]+(?: x [a-z]+)?|[A-Z][a-z]+_[a-z]+ x [A-Z][a-z]+_[a-z]+)
|
|
168
|
-
* $2a: x [a-z]+
|
|
169
|
-
* $2b hybrid: [a-z][^\s.]+(?: x [a-z]+)?
|
|
170
|
-
* $2c intergeneric hybrid: [A-Z][a-z]+_[a-z]+ x [A-Z][a-z]+_[a-z]+
|
|
168
|
+
* $2 species: (x [a-z-]+|[a-z-][^\s.]+(?: x [a-z-]+)?|[A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+)
|
|
169
|
+
* $2a: x [a-z-]+
|
|
170
|
+
* $2b hybrid: [a-z-][^\s.]+(?: x [a-z-]+)?
|
|
171
|
+
* $2c intergeneric hybrid: [A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+
|
|
171
172
|
*/
|
|
172
|
-
var BINAME_PATTERN = /^(?:((?:x )?[A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?(x [a-z]+|[a-z][^\s.]+(?: x [a-z]+)?|[A-Z][a-z]+_[a-z]+ x [A-Z][a-z]+_[a-z]+)(?= |$)/;
|
|
173
|
+
var BINAME_PATTERN = /^(?:((?:x )?[A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?(x [a-z-]+|[a-z-][^\s.]+(?: x [a-z-]+)?|[A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+)(?= |$)/;
|
|
173
174
|
function compareRanks(a, b) {
|
|
174
175
|
return RANKS.indexOf(a) - RANKS.indexOf(b);
|
|
175
176
|
}
|
package/package.json
CHANGED
|
@@ -62,7 +62,6 @@ const SCOPES: Record<string, [string, string]> = {
|
|
|
62
62
|
'queens': ['dwc:caste', 'queen'],
|
|
63
63
|
'workers': ['dwc:caste', 'worker'],
|
|
64
64
|
'soldiers': ['dwc:caste', 'soldier'],
|
|
65
|
-
'alates': ['dwc:caste', 'alate'],
|
|
66
65
|
'alatae': ['dwc:caste', 'alate'],
|
|
67
66
|
'apterae': ['dwc:caste', 'aptera'],
|
|
68
67
|
'viviparae': ['dwc:caste', 'vivipara'],
|
|
@@ -85,7 +84,9 @@ const PREFIXES = {
|
|
|
85
84
|
'dwciri': 'http://rs.tdwg.org/dwc/iri/',
|
|
86
85
|
'foaf': 'http://xmlns.com/foaf/0.1/',
|
|
87
86
|
'owl': 'http://www.w3.org/2002/07/owl#',
|
|
87
|
+
'rdfs': 'http://www.w3.org/2000/01/rdf-schema#',
|
|
88
88
|
'schema': 'https://schema.org/',
|
|
89
|
+
'xsd': 'http://www.w3.org/2001/XMLSchema#',
|
|
89
90
|
}
|
|
90
91
|
const DWC_FIELDS: Record<string, string> = {
|
|
91
92
|
scientificName: 'dwc:scientificName',
|
|
@@ -100,6 +101,12 @@ const DWC_FIELDS: Record<string, string> = {
|
|
|
100
101
|
verbatimIdentification: 'dwc:verbatimIdentification',
|
|
101
102
|
}
|
|
102
103
|
const GBIF_RANKS = ['kingdom', 'phylum', 'class', 'order', 'family', 'genus', 'species', 'subspecies']
|
|
104
|
+
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']
|
|
105
|
+
const STATUSES: Record<string, string> = {
|
|
106
|
+
'accepted': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/accepted',
|
|
107
|
+
'heterotypic synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/heterotypicSynonym',
|
|
108
|
+
'synonym': 'http://rs.gbif.org/vocabulary/gbif/taxonomicStatus/synonym',
|
|
109
|
+
}
|
|
103
110
|
|
|
104
111
|
function getCoveringTaxon (taxa: catalog.Entity[]): string|null {
|
|
105
112
|
if (taxa.length === 1 && !taxa[0].has('gbif')) {
|
|
@@ -165,6 +172,18 @@ function makeScientificNameUri (id: string): NodeObject {
|
|
|
165
172
|
return { '@id': `${PREFIX}resource/${resource}#${id}` }
|
|
166
173
|
}
|
|
167
174
|
|
|
175
|
+
function makeTaxonRankUri (rank: string): NodeObject|string {
|
|
176
|
+
if (GBIF_VOCAB_RANKS.includes(rank)) {
|
|
177
|
+
return { '@id': `http://rs.gbif.org/vocabulary/gbif/rank/${rank}` }
|
|
178
|
+
} else {
|
|
179
|
+
return rank
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function makeTaxonomicStatusUri (status: string): NodeObject {
|
|
184
|
+
return { '@id': STATUSES[status] as string }
|
|
185
|
+
}
|
|
186
|
+
|
|
168
187
|
function makeLinkedDataForAuthor (author: catalog.Entity): NodeObject {
|
|
169
188
|
const node: NodeObject = {
|
|
170
189
|
'@id': `${PREFIX}author/${author.get('id')}`,
|
|
@@ -195,7 +214,7 @@ function makeLinkedDataForPlace (place: catalog.Entity): NodeObject {
|
|
|
195
214
|
|
|
196
215
|
function makeLinkedDataForPublisher (publisher: catalog.Entity): NodeObject {
|
|
197
216
|
const node: NodeObject = {
|
|
198
|
-
'@id': `${PREFIX}
|
|
217
|
+
'@id': `${PREFIX}publisher/${publisher.get('id')}`,
|
|
199
218
|
'@type': 'foaf:Organization',
|
|
200
219
|
'foaf:name': publisher.get('display_name')
|
|
201
220
|
}
|
|
@@ -215,7 +234,7 @@ function makeLinkedDataForTaxon (taxon: catalog.Entity): NodeObject {
|
|
|
215
234
|
}
|
|
216
235
|
|
|
217
236
|
if (taxon.has('rank')) {
|
|
218
|
-
node['dwc:taxonRank'] = taxon.get('rank')
|
|
237
|
+
node['dwc:taxonRank'] = makeTaxonRankUri(taxon.get('rank') as string)
|
|
219
238
|
}
|
|
220
239
|
|
|
221
240
|
const ids = []
|
|
@@ -249,7 +268,7 @@ function makeLinkedDataForTaxa (files: Catalog): NodeObject[] {
|
|
|
249
268
|
for (let i = 0; i < ancestors.length; i++) {
|
|
250
269
|
gbifTaxa[ancestors[i]] = {
|
|
251
270
|
...makeGbifUri(ancestors[i]),
|
|
252
|
-
'dwc:taxonRank': GBIF_RANKS[i],
|
|
271
|
+
'dwc:taxonRank': makeTaxonRankUri(GBIF_RANKS[i]),
|
|
253
272
|
'dwc:parentNameUsageID': i ? makeGbifUri(ancestors[i - 1]) : { '@id': `${PREFIX}taxon/T141` }
|
|
254
273
|
}
|
|
255
274
|
}
|
|
@@ -262,7 +281,7 @@ function makeLinkedDataForTaxa (files: Catalog): NodeObject[] {
|
|
|
262
281
|
for (const child of children) {
|
|
263
282
|
gbifTaxa[child] = {
|
|
264
283
|
...makeGbifUri(child),
|
|
265
|
-
'dwc:taxonRank': childRank,
|
|
284
|
+
'dwc:taxonRank': makeTaxonRankUri(childRank),
|
|
266
285
|
}
|
|
267
286
|
}
|
|
268
287
|
}
|
|
@@ -288,6 +307,14 @@ function makeLinkedDataForScientificName (name: AmendedTaxon): NodeObject {
|
|
|
288
307
|
}
|
|
289
308
|
}
|
|
290
309
|
|
|
310
|
+
if (GBIF_VOCAB_RANKS.includes(name.taxonRank)) {
|
|
311
|
+
node[DWC_FIELDS.taxonRank] = makeTaxonRankUri(name.taxonRank)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (name.taxonomicStatus) {
|
|
315
|
+
node[DWC_FIELDS.taxonomicStatus] = makeTaxonomicStatusUri(name.taxonomicStatus)
|
|
316
|
+
}
|
|
317
|
+
|
|
291
318
|
if (name.acceptedNameUsageID) {
|
|
292
319
|
node['dwc:acceptedNameUsageID'] = makeScientificNameUri(name.acceptedNameUsageID)
|
|
293
320
|
}
|
|
@@ -435,7 +462,10 @@ function makeLinkedDataForWork (work: catalog.Entity, files: Catalog): NodeObjec
|
|
|
435
462
|
|
|
436
463
|
if (work.has('title')) {
|
|
437
464
|
const title = work.get('title') as string[]
|
|
438
|
-
|
|
465
|
+
if (title.length > languages.length) {
|
|
466
|
+
title.splice(0, title.length, title.join('; '))
|
|
467
|
+
}
|
|
468
|
+
node['dcterms:title'] = title.map((value, i) => ({ '@value': value, '@language': languages[i] }))
|
|
439
469
|
}
|
|
440
470
|
|
|
441
471
|
if (work.has('pages') && (work.get('pages') as string).includes('-')) {
|
|
@@ -462,7 +492,7 @@ function makeLinkedDataForWork (work: catalog.Entity, files: Catalog): NodeObjec
|
|
|
462
492
|
|
|
463
493
|
const handle = urls.find(url => url.startsWith(HANDLE_PREFIX))
|
|
464
494
|
if (handle) {
|
|
465
|
-
node['bibo:handle'] = handle.slice(HANDLE_PREFIX.length)
|
|
495
|
+
node['bibo:handle'] = { '@id': handle.slice(HANDLE_PREFIX.length) }
|
|
466
496
|
}
|
|
467
497
|
|
|
468
498
|
node['schema:url'] = urls.map(url => ({ '@id': url }))
|
|
@@ -479,7 +509,17 @@ function makeLinkedDataForWork (work: catalog.Entity, files: Catalog): NodeObjec
|
|
|
479
509
|
}
|
|
480
510
|
|
|
481
511
|
if (work.has('date')) {
|
|
482
|
-
|
|
512
|
+
const date = work.get('date') as string
|
|
513
|
+
let dateType = 'rdfs:Literal'
|
|
514
|
+
if (date.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
515
|
+
dateType = 'xsd:date'
|
|
516
|
+
} else if (date.match(/^\d{4}-\d{2}$/)) {
|
|
517
|
+
dateType = 'xsd:gYearMonth'
|
|
518
|
+
} else if (date.match(/^\d{4}$/)) {
|
|
519
|
+
dateType = 'xsd:gYear'
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
node['dcterms:issued'] = { '@value': work.get('date'), '@type': dateType }
|
|
483
523
|
}
|
|
484
524
|
|
|
485
525
|
if (work.has('publisher')) {
|
|
@@ -531,9 +571,15 @@ function makeLinkedDataForWork (work: catalog.Entity, files: Catalog): NodeObjec
|
|
|
531
571
|
node['bibo:pages'] = pages
|
|
532
572
|
|
|
533
573
|
const [start, end] = range.map(part => parseInt(part))
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
574
|
+
if (!isNaN(start)) {
|
|
575
|
+
node['bibo:pageStart'] = start
|
|
576
|
+
}
|
|
577
|
+
if (!isNaN(end)) {
|
|
578
|
+
node['bibo:pageEnd'] = end
|
|
579
|
+
}
|
|
580
|
+
if (!isNaN(start) && !isNaN(end)) {
|
|
581
|
+
node['bibo:numPages'] = end - start + 1
|
|
582
|
+
}
|
|
537
583
|
} else {
|
|
538
584
|
node['bibo:pages'] = pages
|
|
539
585
|
}
|
|
@@ -600,7 +646,7 @@ function makeLinkedDataForWorks (files: Catalog): NodeObject[] {
|
|
|
600
646
|
const originals = work.get('version_of') as string[]
|
|
601
647
|
const originalLanguages = originals.map(id => ((works.get(id) as catalog.Entity).get('language') as string[]).join())
|
|
602
648
|
node['bibo:translationOf'] = originals.filter((_, i) => originalLanguages[i] !== language.join()).map(makeWorkUri)
|
|
603
|
-
node['dcterms:isVersionOf'] = originals.map(makeWorkUri)
|
|
649
|
+
node['dcterms:isVersionOf'] = originals.filter(id => work.get('id') !== id).map(makeWorkUri)
|
|
604
650
|
}
|
|
605
651
|
|
|
606
652
|
let resourceIndex = 1
|
|
@@ -7,7 +7,7 @@ export class Author extends Entity {
|
|
|
7
7
|
name: { required: true, multiple: true },
|
|
8
8
|
id: { required: true, multiple: false, format: FORMATS.AUTHOR_ID },
|
|
9
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
10
|
-
display_name: { required:
|
|
10
|
+
display_name: { required: true, multiple: false },
|
|
11
11
|
full_names: { required: false, multiple: true },
|
|
12
12
|
duplicate_of: { required: false, multiple: false, format: FORMATS.AUTHOR_ID }
|
|
13
13
|
})
|
|
@@ -7,7 +7,7 @@ export class Place extends Entity {
|
|
|
7
7
|
name: { required: true, multiple: false },
|
|
8
8
|
id: { required: true, multiple: false, format: FORMATS.PLACE_ID },
|
|
9
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
10
|
-
display_name: { required:
|
|
10
|
+
display_name: { required: true, multiple: false },
|
|
11
11
|
duplicate_of: { required: false, multiple: false, format: FORMATS.PLACE_ID }
|
|
12
12
|
})
|
|
13
13
|
}
|
|
@@ -7,7 +7,7 @@ export class Publisher extends Entity {
|
|
|
7
7
|
name: { required: true, multiple: true },
|
|
8
8
|
id: { required: true, multiple: false, format: FORMATS.PUBLISHER_ID },
|
|
9
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
10
|
-
display_name: { required:
|
|
10
|
+
display_name: { required: true, multiple: false },
|
|
11
11
|
full_names: { required: false, multiple: true },
|
|
12
12
|
duplicate_of: { required: false, multiple: false, format: FORMATS.PUBLISHER_ID }
|
|
13
13
|
})
|
|
@@ -58,7 +58,8 @@ const DWC_RANKS: DwcRank[] = [
|
|
|
58
58
|
const FLAGS: ResourceFlag[] = [
|
|
59
59
|
'MISSING_TAXA',
|
|
60
60
|
'MISSING_PARENT_TAXA',
|
|
61
|
-
'MISSING_SYNONYMS'
|
|
61
|
+
'MISSING_SYNONYMS',
|
|
62
|
+
'MISSING_AUTHORSHIP'
|
|
62
63
|
]
|
|
63
64
|
|
|
64
65
|
const TAXONOMIC_STATUS: Record<string, TaxonStatus> = {
|
|
@@ -108,7 +109,7 @@ const NAME_PATTERN = new RegExp(
|
|
|
108
109
|
// $2 optional author citation
|
|
109
110
|
'(?: ' +
|
|
110
111
|
// but not auct(t)., etc.
|
|
111
|
-
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
112
|
+
'(?!auctt?\\.|(?:syn|comb|sp|spec|nom|gen|subgen)\\. n(?:ov)?\\.|s(?:ens[.u]|\\.)|in part|partim)' +
|
|
112
113
|
'(' +
|
|
113
114
|
// $2.1 anything in parentheses, followed by optional revising author(s)
|
|
114
115
|
'\\(.+?\\)(?:\\s+' + SIMPLE_AUTHOR_PATTERN + ')?' +
|
|
@@ -130,12 +131,12 @@ const NAME_PATTERN = new RegExp(
|
|
|
130
131
|
* $1 genus+subgenus (+ trailing space): (?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?
|
|
131
132
|
* $1.1 genus: ((?:x )?[A-Z]\S+)
|
|
132
133
|
* $1.2 subgenus: (?:\(([A-Z]\S+?)\) )?
|
|
133
|
-
* $2 species: (x [a-z]+|[a-z][^\s.]+(?: x [a-z]+)?|[A-Z][a-z]+_[a-z]+ x [A-Z][a-z]+_[a-z]+)
|
|
134
|
-
* $2a: x [a-z]+
|
|
135
|
-
* $2b hybrid: [a-z][^\s.]+(?: x [a-z]+)?
|
|
136
|
-
* $2c intergeneric hybrid: [A-Z][a-z]+_[a-z]+ x [A-Z][a-z]+_[a-z]+
|
|
134
|
+
* $2 species: (x [a-z-]+|[a-z-][^\s.]+(?: x [a-z-]+)?|[A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+)
|
|
135
|
+
* $2a: x [a-z-]+
|
|
136
|
+
* $2b hybrid: [a-z-][^\s.]+(?: x [a-z-]+)?
|
|
137
|
+
* $2c intergeneric hybrid: [A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+
|
|
137
138
|
*/
|
|
138
|
-
const BINAME_PATTERN = /^(?:((?:x )?[A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?(x [a-z]+|[a-z][^\s.]+(?: x [a-z]+)?|[A-Z][a-z]+_[a-z]+ x [A-Z][a-z]+_[a-z]+)(?= |$)/
|
|
139
|
+
const BINAME_PATTERN = /^(?:((?:x )?[A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?(x [a-z-]+|[a-z-][^\s.]+(?: x [a-z-]+)?|[A-Z][a-z]+_[a-z-]+ x [A-Z][a-z]+_[a-z-]+)(?= |$)/
|
|
139
140
|
|
|
140
141
|
function compareRanks (a: Rank, b: Rank): number {
|
|
141
142
|
return RANKS.indexOf(a) - RANKS.indexOf(b)
|