@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 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
- // duplicate_of: { required: false, multiple: false, format: FORMATS.ID }
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-z]\S+)
125
+ * $2 species: ((?:x )?[a-z0-9-]+)
126
126
  */
127
- var BINAME_PATTERN = /^(?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?([a-z]\S+) ?/;
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).replace(/^(st|r|ab|f|var|ssp|subsp)\. /, '');
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
- var isIndet = Array.from(INDET_SUFFIXES).some(function (suffix) { return name_1.endsWith(' ' + suffix); });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larsgw/formica",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
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 @@ 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
- // duplicate_of: { required: false, multiple: false, format: FORMATS.ID }
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-z]\S+)
123
+ * $2 species: ((?:x )?[a-z0-9-]+)
124
124
  */
125
- const BINAME_PATTERN = /^(?:([A-Z]\S+) (?:\(([A-Z]\S+?)\) )?)?([a-z]\S+) ?/
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).replace(/^(st|r|ab|f|var|ssp|subsp)\. /, '')
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' || isIndet) {
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
  })