@larsgw/formica 0.4.0 → 0.4.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 +20 -0
- package/lib/catalog/tables/work.js +1 -1
- package/lib/resources/parse-text.js +14 -9
- package/package.json +1 -1
- package/src/catalog/tables/work.ts +1 -1
- package/src/resources/parse-text.ts +14 -9
- package/test/resources.js +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## [0.4.2](https://github.com/identification-resources/formica/compare/v0.4.1...v0.4.2) (2023-09-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **resources:** allow simpler intraspecific synonyms ([978186c](https://github.com/identification-resources/formica/commit/978186cbaf469f3085c24c2e8bc8ffb1f83f2799)), closes [#9](https://github.com/identification-resources/formica/issues/9)
|
|
7
|
+
* **resources:** correct synonym rank determintation ([766ea2a](https://github.com/identification-resources/formica/commit/766ea2af14ff3aa64a7a2e19ed096836d38182cf))
|
|
8
|
+
* **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)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## [0.4.1](https://github.com/identification-resources/formica/compare/v0.4.0...v0.4.1) (2023-09-07)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **catalog:** fix typo in duplicate_of schema ([fbb6333](https://github.com/identification-resources/formica/commit/fbb6333d10dd6f5846856ea8de81d941ce539d21))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
1
21
|
# [0.4.0](https://github.com/identification-resources/formica/compare/v0.3.1...v0.4.0) (2023-09-07)
|
|
2
22
|
|
|
3
23
|
|
|
@@ -52,7 +52,7 @@ var Work = /** @class */ (function (_super) {
|
|
|
52
52
|
listed_in: { required: false, multiple: true, format: value_1.FORMATS.ID },
|
|
53
53
|
part_of: { required: false, multiple: true, format: value_1.FORMATS.ID },
|
|
54
54
|
version_of: { required: false, multiple: true, format: value_1.FORMATS.ID },
|
|
55
|
-
|
|
55
|
+
duplicate_of: { required: false, multiple: false, format: value_1.FORMATS.ID }
|
|
56
56
|
}) || this;
|
|
57
57
|
}
|
|
58
58
|
Work.prototype.deriveFields = function () {
|
|
@@ -122,9 +122,9 @@ var NAME_PATTERN = new RegExp('^' +
|
|
|
122
122
|
* $1 genus+subgenus (+ trailing space): (?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?
|
|
123
123
|
* $1.1 genus: ([A-Z]\S+)
|
|
124
124
|
* $1.2 subgenus: (?:\(([A-Z]\S+?)\) )?
|
|
125
|
-
* $2 species: ([a-
|
|
125
|
+
* $2 species: ((?:x )?[a-z0-9-]+)
|
|
126
126
|
*/
|
|
127
|
-
var BINAME_PATTERN = /^(?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?([a-
|
|
127
|
+
var BINAME_PATTERN = /^(?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?((?:x )?[a-z0-9-]+)(?= |$)/;
|
|
128
128
|
function compareRanks(a, b) {
|
|
129
129
|
return RANKS.indexOf(a) - RANKS.indexOf(b);
|
|
130
130
|
}
|
|
@@ -136,14 +136,14 @@ function isUpperCase(name) {
|
|
|
136
136
|
}
|
|
137
137
|
function getSynonymRank(name, rank) {
|
|
138
138
|
var BINAME_PATTERN = /^([A-Z]\S+ (\([A-Z]\S+\) )?)?(x )?[a-z0-9-]+(?= |$)/;
|
|
139
|
-
if (!BINAME_PATTERN.test(name)) {
|
|
140
|
-
return rank;
|
|
141
|
-
}
|
|
142
139
|
var rest = name.replace(BINAME_PATTERN, '');
|
|
143
|
-
var rankPrefix = rest.match(/^ (st|r|ab|f|var|ssp|subsp)\. /);
|
|
140
|
+
var rankPrefix = rest.match(/^(?: |^)(st|r|ab|f|var|ssp|subsp)\. /);
|
|
144
141
|
if (rankPrefix) {
|
|
145
142
|
return RANK_LABELS_REVERSE[rankPrefix[1]];
|
|
146
143
|
}
|
|
144
|
+
else if (!BINAME_PATTERN.test(name)) {
|
|
145
|
+
return rank;
|
|
146
|
+
}
|
|
147
147
|
else if (/^ (?!sensu)[a-z0-9-]+($| )/.test(rest)) {
|
|
148
148
|
return 'subspecies';
|
|
149
149
|
}
|
|
@@ -221,8 +221,9 @@ function parseName(name, rank, parent) {
|
|
|
221
221
|
if (compareRanks('species', rank) < 0) {
|
|
222
222
|
var speciesPrefix = parseContext.specificEpithet + ' ';
|
|
223
223
|
if (name.startsWith(speciesPrefix)) {
|
|
224
|
-
name = name.slice(speciesPrefix.length)
|
|
224
|
+
name = name.slice(speciesPrefix.length);
|
|
225
225
|
}
|
|
226
|
+
name = name.replace(/^(st|r|ab|f|var|ssp|subsp)\. /, '');
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
// Hybrids
|
|
@@ -400,6 +401,11 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
400
401
|
id++;
|
|
401
402
|
return "continue";
|
|
402
403
|
}
|
|
404
|
+
// Do not process "indet" lines further, as they only serve to indicate
|
|
405
|
+
// that subtaxa are explicitely omitted
|
|
406
|
+
if (Array.from(INDET_SUFFIXES).some(function (suffix) { return line.endsWith(' ' + suffix); })) {
|
|
407
|
+
return "continue";
|
|
408
|
+
}
|
|
403
409
|
var lineIndent = line.match(/^ */)[0].length;
|
|
404
410
|
if (lineIndent > groupIndent) {
|
|
405
411
|
// Do not count synonyms as parents (unless this is correcting a typo in the synonym)
|
|
@@ -431,8 +437,7 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
431
437
|
var rank = resource.metadata.levels[groupIndent / 2];
|
|
432
438
|
var item = parseName(name_1, rank, parent_1);
|
|
433
439
|
var isSynonym = item.taxonomicStatus !== 'accepted';
|
|
434
|
-
|
|
435
|
-
if (item.taxonomicStatus === 'incorrect' || isIndet) {
|
|
440
|
+
if (item.taxonomicStatus === 'incorrect') {
|
|
436
441
|
return "continue";
|
|
437
442
|
}
|
|
438
443
|
if (type === diff_resource_1.ResourceDiffType.Added) {
|
package/package.json
CHANGED
|
@@ -34,7 +34,7 @@ export class Work extends Entity {
|
|
|
34
34
|
listed_in: { required: false, multiple: true, format: FORMATS.ID },
|
|
35
35
|
part_of: { required: false, multiple: true, format: FORMATS.ID },
|
|
36
36
|
version_of: { required: false, multiple: true, format: FORMATS.ID },
|
|
37
|
-
|
|
37
|
+
duplicate_of: { required: false, multiple: false, format: FORMATS.ID }
|
|
38
38
|
})
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -120,9 +120,9 @@ const NAME_PATTERN = new RegExp(
|
|
|
120
120
|
* $1 genus+subgenus (+ trailing space): (?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?
|
|
121
121
|
* $1.1 genus: ([A-Z]\S+)
|
|
122
122
|
* $1.2 subgenus: (?:\(([A-Z]\S+?)\) )?
|
|
123
|
-
* $2 species: ([a-
|
|
123
|
+
* $2 species: ((?:x )?[a-z0-9-]+)
|
|
124
124
|
*/
|
|
125
|
-
const BINAME_PATTERN = /^(?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?([a-
|
|
125
|
+
const BINAME_PATTERN = /^(?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?((?:x )?[a-z0-9-]+)(?= |$)/
|
|
126
126
|
|
|
127
127
|
function compareRanks (a: Rank, b: Rank): number {
|
|
128
128
|
return RANKS.indexOf(a) - RANKS.indexOf(b)
|
|
@@ -138,13 +138,12 @@ function isUpperCase (name: string): boolean {
|
|
|
138
138
|
|
|
139
139
|
function getSynonymRank (name: string, rank: Rank): Rank {
|
|
140
140
|
const BINAME_PATTERN = /^([A-Z]\S+ (\([A-Z]\S+\) )?)?(x )?[a-z0-9-]+(?= |$)/
|
|
141
|
-
if (!BINAME_PATTERN.test(name)) {
|
|
142
|
-
return rank
|
|
143
|
-
}
|
|
144
141
|
const rest = name.replace(BINAME_PATTERN, '')
|
|
145
|
-
const rankPrefix = rest.match(/^ (st|r|ab|f|var|ssp|subsp)\. /)
|
|
142
|
+
const rankPrefix = rest.match(/^(?: |^)(st|r|ab|f|var|ssp|subsp)\. /)
|
|
146
143
|
if (rankPrefix) {
|
|
147
144
|
return RANK_LABELS_REVERSE[rankPrefix[1]] as string
|
|
145
|
+
} else if (!BINAME_PATTERN.test(name)) {
|
|
146
|
+
return rank
|
|
148
147
|
} else if (/^ (?!sensu)[a-z0-9-]+($| )/.test(rest)) {
|
|
149
148
|
return 'subspecies'
|
|
150
149
|
} else {
|
|
@@ -220,8 +219,9 @@ function parseName (name: string, rank: Rank, parent: WorkingTaxon): WorkingTaxo
|
|
|
220
219
|
if (compareRanks('species', rank) < 0) {
|
|
221
220
|
const speciesPrefix = parseContext.specificEpithet + ' '
|
|
222
221
|
if (name.startsWith(speciesPrefix)) {
|
|
223
|
-
name = name.slice(speciesPrefix.length)
|
|
222
|
+
name = name.slice(speciesPrefix.length)
|
|
224
223
|
}
|
|
224
|
+
name = name.replace(/^(st|r|ab|f|var|ssp|subsp)\. /, '')
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -422,6 +422,12 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
422
422
|
continue
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
// Do not process "indet" lines further, as they only serve to indicate
|
|
426
|
+
// that subtaxa are explicitely omitted
|
|
427
|
+
if (Array.from(INDET_SUFFIXES).some(suffix => line.endsWith(' ' + suffix))) {
|
|
428
|
+
continue
|
|
429
|
+
}
|
|
430
|
+
|
|
425
431
|
const lineIndent = (line.match(/^ */) as string[])[0].length
|
|
426
432
|
|
|
427
433
|
if (lineIndent > groupIndent) {
|
|
@@ -452,9 +458,8 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
452
458
|
const rank = resource.metadata.levels[groupIndent / 2]
|
|
453
459
|
const item = parseName(name, rank, parent)
|
|
454
460
|
const isSynonym = item.taxonomicStatus !== 'accepted'
|
|
455
|
-
const isIndet = Array.from(INDET_SUFFIXES).some(suffix => name.endsWith(' ' + suffix))
|
|
456
461
|
|
|
457
|
-
if (item.taxonomicStatus === 'incorrect'
|
|
462
|
+
if (item.taxonomicStatus === 'incorrect') {
|
|
458
463
|
continue
|
|
459
464
|
}
|
|
460
465
|
|
package/test/resources.js
CHANGED
|
@@ -57,4 +57,37 @@ Cydnidae
|
|
|
57
57
|
limbosus`, 'T1')
|
|
58
58
|
})
|
|
59
59
|
})
|
|
60
|
+
|
|
61
|
+
await t.test('parses synonyms starting with intraspecific ranks', (t) => {
|
|
62
|
+
const [resource] = resources.parseTextFile(`---
|
|
63
|
+
levels: [species]
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
Lygaeus equestris (Linnaeus, 1758)
|
|
67
|
+
= f. lactans Horváth, 1899
|
|
68
|
+
`, 'T1')
|
|
69
|
+
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Lygaeus equestris f. lactans Horváth, 1899')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
await t.test('parses accepted taxa starting with intraspecific ranks', (t) => {
|
|
73
|
+
const [resource] = resources.parseTextFile(`---
|
|
74
|
+
levels: [species, form]
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
Lygaeus equestris (Linnaeus, 1758)
|
|
78
|
+
f. lactans Horváth, 1899
|
|
79
|
+
`, 'T1')
|
|
80
|
+
assert.strictEqual(resource.taxa['T1:1:2'].scientificName, 'Lygaeus equestris f. lactans Horváth, 1899')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
await t.test('does not validate "indet." lines', (t) => {
|
|
84
|
+
const [resource] = resources.parseTextFile(`---
|
|
85
|
+
levels: [genus, species]
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
Drymus
|
|
89
|
+
Unknown sp.
|
|
90
|
+
`, 'T1')
|
|
91
|
+
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Drymus')
|
|
92
|
+
})
|
|
60
93
|
})
|