@larsgw/formica 0.6.8 → 0.7.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 +28 -0
- package/lib/resources/diff-resource.d.ts +1 -1
- package/lib/resources/diff-resource.js +8 -3
- package/lib/resources/parse-text.js +40 -28
- package/package.json +1 -1
- package/src/module.d.ts +2 -1
- package/src/resources/diff-resource.ts +16 -6
- package/src/resources/parse-text.ts +39 -24
- package/test/resources.js +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## [0.7.1](https://github.com/identification-resources/formica/compare/v0.7.0...v0.7.1) (2025-03-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **resources:** add 'subphylum' rank ([8b5243b](https://github.com/identification-resources/formica/commit/8b5243bfd79b6486c613ca1178f1e1c9ec3f43a4))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# [0.7.0](https://github.com/identification-resources/formica/compare/v0.6.8...v0.7.0) (2025-03-14)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **resources:** improve diff regarding indet lines ([2615437](https://github.com/identification-resources/formica/commit/26154374f50d680c5f3e187cd6a1a5eadbac6b5d))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **resources:** change syntax of "indet" line ([d53363e](https://github.com/identification-resources/formica/commit/d53363e65bfa77a2eb247999316621f9ca0bff59))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
* **resources:** "indet" lines now have to be prefixed with "[indet]"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
1
29
|
## [0.6.8](https://github.com/identification-resources/formica/compare/v0.6.7...v0.6.8) (2025-03-13)
|
|
2
30
|
|
|
3
31
|
|
|
@@ -4,4 +4,4 @@ export declare enum ResourceDiffType {
|
|
|
4
4
|
Modified = "~",
|
|
5
5
|
Unchanged = "="
|
|
6
6
|
}
|
|
7
|
-
export declare function createDiff(a: string, b: string, tokenize?: ResourceDiffTokenizer):
|
|
7
|
+
export declare function createDiff(a: string, b: string, tokenize?: ResourceDiffTokenizer): ResourceDiff;
|
|
@@ -68,6 +68,7 @@ function gitTokenize(text) {
|
|
|
68
68
|
return text.match(/\S+|\n|[\r\t\f\v \u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/g);
|
|
69
69
|
}
|
|
70
70
|
function createDiff(a, b, tokenize) {
|
|
71
|
+
var _a, _b;
|
|
71
72
|
if (tokenize === void 0) { tokenize = gitTokenize; }
|
|
72
73
|
var X = tokenize(a.trimEnd());
|
|
73
74
|
var Y = tokenize(b.trimEnd());
|
|
@@ -106,7 +107,7 @@ function createDiff(a, b, tokenize) {
|
|
|
106
107
|
// Start of line
|
|
107
108
|
if (line === null) {
|
|
108
109
|
deletedNewlines = 0;
|
|
109
|
-
line = {
|
|
110
|
+
line = { type: change.type };
|
|
110
111
|
}
|
|
111
112
|
// End of line (could be same token)
|
|
112
113
|
if (change.text === '\n') {
|
|
@@ -132,7 +133,8 @@ function createDiff(a, b, tokenize) {
|
|
|
132
133
|
// Add placeholders for deleted lines
|
|
133
134
|
while (deletedNewlines--) {
|
|
134
135
|
lines.push({
|
|
135
|
-
text:
|
|
136
|
+
text: undefined,
|
|
137
|
+
original: '',
|
|
136
138
|
type: ResourceDiffType.Deleted
|
|
137
139
|
});
|
|
138
140
|
}
|
|
@@ -144,7 +146,10 @@ function createDiff(a, b, tokenize) {
|
|
|
144
146
|
line.type = ResourceDiffType.Modified;
|
|
145
147
|
}
|
|
146
148
|
if (change.type !== ResourceDiffType.Deleted) {
|
|
147
|
-
line.text
|
|
149
|
+
line.text = ((_a = line.text) !== null && _a !== void 0 ? _a : '') + change.text;
|
|
150
|
+
}
|
|
151
|
+
if (change.type !== ResourceDiffType.Added) {
|
|
152
|
+
line.original = ((_b = line.original) !== null && _b !== void 0 ? _b : '') + change.text;
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
return lines;
|
|
@@ -17,6 +17,7 @@ var resource_1 = require("./resource");
|
|
|
17
17
|
var diff_resource_1 = require("./diff-resource");
|
|
18
18
|
var RANKS = [
|
|
19
19
|
'phylum',
|
|
20
|
+
'subphylum',
|
|
20
21
|
'class',
|
|
21
22
|
'infraclass',
|
|
22
23
|
'superorder',
|
|
@@ -74,13 +75,6 @@ var TAXONOMIC_STATUS = {
|
|
|
74
75
|
'+': 'heterotypic synonym',
|
|
75
76
|
'=': 'synonym'
|
|
76
77
|
};
|
|
77
|
-
var INDET_SUFFIXES = new Set([
|
|
78
|
-
'sp.',
|
|
79
|
-
'spec.',
|
|
80
|
-
'indet.',
|
|
81
|
-
'sp. indet.',
|
|
82
|
-
'spec. indet.'
|
|
83
|
-
]);
|
|
84
78
|
var RANK_LABELS = {
|
|
85
79
|
'subspecies': 'subsp.',
|
|
86
80
|
'variety': 'var.',
|
|
@@ -281,7 +275,7 @@ function parseName(name, rank, parent) {
|
|
|
281
275
|
item.scientificNameAuthorship = capitalizeAuthors(citation);
|
|
282
276
|
item.taxonRemarks = notes;
|
|
283
277
|
item.taxonRank = rank;
|
|
284
|
-
if (/[^\p{L}0-9\u{00D7}\- ]/u.test(taxon)
|
|
278
|
+
if (/[^\p{L}0-9\u{00D7}\- ]/u.test(taxon)) {
|
|
285
279
|
throw new Error("Taxon name contains unexpected characters: \"".concat(taxon, "\""));
|
|
286
280
|
}
|
|
287
281
|
// Validate names and recompose binomial and trinomial names
|
|
@@ -457,6 +451,16 @@ function parseResource(resource) {
|
|
|
457
451
|
var content = rest.join('');
|
|
458
452
|
return [config, content];
|
|
459
453
|
}
|
|
454
|
+
function getIndentation(line) {
|
|
455
|
+
return line.match(/^ */)[0].length;
|
|
456
|
+
}
|
|
457
|
+
function isIndetLine(line, indent) {
|
|
458
|
+
if (indent === undefined) {
|
|
459
|
+
indent = getIndentation(line);
|
|
460
|
+
}
|
|
461
|
+
line = line.slice(indent);
|
|
462
|
+
return line.startsWith('[indet]');
|
|
463
|
+
}
|
|
460
464
|
function parseResourceContent(content, resource, oldIds) {
|
|
461
465
|
var idBase = "".concat(resource.id, ":");
|
|
462
466
|
var data = resource.taxa;
|
|
@@ -465,20 +469,19 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
465
469
|
var groupIndent = 0;
|
|
466
470
|
var previousId = '';
|
|
467
471
|
var newIdOffset = Math.max.apply(Math, oldIds);
|
|
468
|
-
var
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
return "continue";
|
|
472
|
+
for (var _i = 0, content_1 = content; _i < content_1.length; _i++) {
|
|
473
|
+
var line = content_1[_i];
|
|
474
|
+
if (line.type === diff_resource_1.ResourceDiffType.Deleted) {
|
|
475
|
+
// Increase id counter for removed line unless it was an "indet line"
|
|
476
|
+
if (!isIndetLine(line.original)) {
|
|
477
|
+
id++;
|
|
478
|
+
}
|
|
479
|
+
continue;
|
|
477
480
|
}
|
|
478
|
-
var lineIndent = line.
|
|
481
|
+
var lineIndent = getIndentation(line.text);
|
|
479
482
|
if (lineIndent > groupIndent) {
|
|
480
483
|
// Do not count synonyms as parents (unless this is correcting a typo in the synonym)
|
|
481
|
-
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted' || /^( {2})+> /.test(line)) {
|
|
484
|
+
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted' || /^( {2})+> /.test(line.text)) {
|
|
482
485
|
parents.push(previousId);
|
|
483
486
|
}
|
|
484
487
|
else {
|
|
@@ -500,17 +503,26 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
500
503
|
parents = parents.slice(0, lineIndent / 2);
|
|
501
504
|
groupIndent = lineIndent;
|
|
502
505
|
}
|
|
506
|
+
// Do not process "indet" lines further, as they only serve to indicate
|
|
507
|
+
// that subtaxa are explicitely omitted
|
|
508
|
+
if (isIndetLine(line.text, lineIndent)) {
|
|
509
|
+
// If the line was previously not and ndet line, increase the id counter
|
|
510
|
+
if (line.type === diff_resource_1.ResourceDiffType.Modified && !isIndetLine(line.original)) {
|
|
511
|
+
id++;
|
|
512
|
+
}
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
503
515
|
var parentId = parents.reduce(function (grandparent, parent) { return parent || grandparent; }, null);
|
|
504
516
|
var parent_1 = parentId === null ? {} : data[parentId];
|
|
505
|
-
var name_1 = line.slice(groupIndent);
|
|
517
|
+
var name_1 = line.text.slice(groupIndent);
|
|
506
518
|
var rank = resource.metadata.levels[groupIndent / 2];
|
|
507
519
|
var item = parseName(name_1, rank, parent_1);
|
|
508
520
|
var isSynonym = item.taxonomicStatus !== 'accepted';
|
|
509
521
|
// Add higher classification info
|
|
510
522
|
var itemAsObject = item;
|
|
511
523
|
var parentAsObject = parent_1;
|
|
512
|
-
for (var
|
|
513
|
-
var rank_1 = DWC_RANKS_1[
|
|
524
|
+
for (var _a = 0, DWC_RANKS_1 = DWC_RANKS; _a < DWC_RANKS_1.length; _a++) {
|
|
525
|
+
var rank_1 = DWC_RANKS_1[_a];
|
|
514
526
|
itemAsObject[rank_1] = undefined;
|
|
515
527
|
if (parentAsObject[rank_1]) {
|
|
516
528
|
itemAsObject[rank_1] = parentAsObject[rank_1];
|
|
@@ -542,10 +554,14 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
542
554
|
parentAsObject[key] = itemAsObject[key];
|
|
543
555
|
}
|
|
544
556
|
}
|
|
545
|
-
|
|
557
|
+
continue;
|
|
546
558
|
}
|
|
547
559
|
// Set identifiers
|
|
548
|
-
if (type === diff_resource_1.ResourceDiffType.Added) {
|
|
560
|
+
if (line.type === diff_resource_1.ResourceDiffType.Added) {
|
|
561
|
+
newIdOffset++;
|
|
562
|
+
item.scientificNameID = idBase + newIdOffset.toString();
|
|
563
|
+
}
|
|
564
|
+
else if (line.type === diff_resource_1.ResourceDiffType.Modified && isIndetLine(line.original)) {
|
|
549
565
|
newIdOffset++;
|
|
550
566
|
item.scientificNameID = idBase + newIdOffset.toString();
|
|
551
567
|
}
|
|
@@ -560,10 +576,6 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
560
576
|
item.acceptedNameUsage = isSynonym ? parent_1.scientificName : undefined;
|
|
561
577
|
item.collectionCode = idBase.slice(0, -1);
|
|
562
578
|
data[item.scientificNameID] = item;
|
|
563
|
-
};
|
|
564
|
-
for (var _i = 0, content_1 = content; _i < content_1.length; _i++) {
|
|
565
|
-
var _a = content_1[_i], line = _a.text, type = _a.type;
|
|
566
|
-
_loop_1(line, type);
|
|
567
579
|
}
|
|
568
580
|
return resource;
|
|
569
581
|
}
|
package/package.json
CHANGED
package/src/module.d.ts
CHANGED
|
@@ -5,7 +5,12 @@ export enum ResourceDiffType {
|
|
|
5
5
|
Unchanged = '='
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface DiffPart {
|
|
9
|
+
text: string,
|
|
10
|
+
type: ResourceDiffType
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function LCS (X: string[], Y: string[]): DiffPart[] {
|
|
9
14
|
const m = X.length
|
|
10
15
|
const n = Y.length
|
|
11
16
|
|
|
@@ -57,7 +62,7 @@ function gitTokenize (text: string): string[] {
|
|
|
57
62
|
return text.match(/\S+|\n|[\r\t\f\v \u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/g) as string[]
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
export function createDiff (a: string, b: string, tokenize: ResourceDiffTokenizer = gitTokenize):
|
|
65
|
+
export function createDiff (a: string, b: string, tokenize: ResourceDiffTokenizer = gitTokenize): ResourceDiff {
|
|
61
66
|
const X = tokenize(a.trimEnd())
|
|
62
67
|
const Y = tokenize(b.trimEnd())
|
|
63
68
|
|
|
@@ -94,7 +99,7 @@ export function createDiff (a: string, b: string, tokenize: ResourceDiffTokenize
|
|
|
94
99
|
|
|
95
100
|
// Convert word diff to line diff
|
|
96
101
|
const lines: ResourceDiff = []
|
|
97
|
-
let line = null
|
|
102
|
+
let line: ResourceDiffPart|null = null
|
|
98
103
|
let deletedNewlines = 0
|
|
99
104
|
let nextLineNew = false
|
|
100
105
|
|
|
@@ -102,7 +107,7 @@ export function createDiff (a: string, b: string, tokenize: ResourceDiffTokenize
|
|
|
102
107
|
// Start of line
|
|
103
108
|
if (line === null) {
|
|
104
109
|
deletedNewlines = 0
|
|
105
|
-
line = {
|
|
110
|
+
line = { type: change.type }
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
// End of line (could be same token)
|
|
@@ -132,7 +137,8 @@ export function createDiff (a: string, b: string, tokenize: ResourceDiffTokenize
|
|
|
132
137
|
// Add placeholders for deleted lines
|
|
133
138
|
while (deletedNewlines--) {
|
|
134
139
|
lines.push({
|
|
135
|
-
text:
|
|
140
|
+
text: undefined,
|
|
141
|
+
original: '',
|
|
136
142
|
type: ResourceDiffType.Deleted
|
|
137
143
|
})
|
|
138
144
|
}
|
|
@@ -147,7 +153,11 @@ export function createDiff (a: string, b: string, tokenize: ResourceDiffTokenize
|
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
if (change.type !== ResourceDiffType.Deleted) {
|
|
150
|
-
line.text
|
|
156
|
+
line.text = (line.text ?? '') + change.text
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (change.type !== ResourceDiffType.Added) {
|
|
160
|
+
line.original = (line.original ?? '') + change.text
|
|
151
161
|
}
|
|
152
162
|
}
|
|
153
163
|
|
|
@@ -4,6 +4,7 @@ import { createDiff, ResourceDiffType } from './diff-resource'
|
|
|
4
4
|
|
|
5
5
|
const RANKS: Rank[] = [
|
|
6
6
|
'phylum',
|
|
7
|
+
'subphylum',
|
|
7
8
|
'class',
|
|
8
9
|
'infraclass',
|
|
9
10
|
'superorder',
|
|
@@ -66,14 +67,6 @@ const TAXONOMIC_STATUS: Record<string, TaxonStatus> = {
|
|
|
66
67
|
'=': 'synonym'
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
const INDET_SUFFIXES = new Set([
|
|
70
|
-
'sp.',
|
|
71
|
-
'spec.',
|
|
72
|
-
'indet.',
|
|
73
|
-
'sp. indet.',
|
|
74
|
-
'spec. indet.'
|
|
75
|
-
])
|
|
76
|
-
|
|
77
70
|
const RANK_LABELS: Record<Rank, string> = {
|
|
78
71
|
'subspecies': 'subsp.',
|
|
79
72
|
'variety': 'var.',
|
|
@@ -301,7 +294,7 @@ function parseName (name: string, rank: Rank, parent: WorkingTaxon): WorkingTaxo
|
|
|
301
294
|
item.taxonRemarks = notes
|
|
302
295
|
item.taxonRank = rank
|
|
303
296
|
|
|
304
|
-
if (/[^\p{L}0-9\u{00D7}\- ]/u.test(taxon)
|
|
297
|
+
if (/[^\p{L}0-9\u{00D7}\- ]/u.test(taxon)) {
|
|
305
298
|
throw new Error(`Taxon name contains unexpected characters: "${taxon}"`)
|
|
306
299
|
}
|
|
307
300
|
|
|
@@ -488,6 +481,20 @@ function parseResource (resource: string): [ResourceMetadata, string] {
|
|
|
488
481
|
return [config, content]
|
|
489
482
|
}
|
|
490
483
|
|
|
484
|
+
function getIndentation (line: string): number {
|
|
485
|
+
return (line.match(/^ */) as string[])[0].length
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function isIndetLine (line: string, indent?: number): boolean {
|
|
489
|
+
if (indent === undefined) {
|
|
490
|
+
indent = getIndentation(line)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
line = line.slice(indent)
|
|
494
|
+
|
|
495
|
+
return line.startsWith('[indet]')
|
|
496
|
+
}
|
|
497
|
+
|
|
491
498
|
function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds: number[]): Resource {
|
|
492
499
|
const idBase = `${resource.id}:`
|
|
493
500
|
|
|
@@ -498,23 +505,19 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
498
505
|
let previousId = ''
|
|
499
506
|
let newIdOffset = Math.max(...oldIds)
|
|
500
507
|
|
|
501
|
-
for (const
|
|
502
|
-
if (type === ResourceDiffType.Deleted) {
|
|
503
|
-
id
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
// Do not process "indet" lines further, as they only serve to indicate
|
|
508
|
-
// that subtaxa are explicitely omitted
|
|
509
|
-
if (Array.from(INDET_SUFFIXES).some(suffix => line.endsWith(' ' + suffix))) {
|
|
508
|
+
for (const line of content) {
|
|
509
|
+
if (line.type === ResourceDiffType.Deleted) {
|
|
510
|
+
// Increase id counter for removed line unless it was an "indet line"
|
|
511
|
+
if (!isIndetLine(line.original as string)) {
|
|
512
|
+
id++
|
|
513
|
+
}
|
|
510
514
|
continue
|
|
511
515
|
}
|
|
512
516
|
|
|
513
|
-
const lineIndent = (line.
|
|
514
|
-
|
|
517
|
+
const lineIndent = getIndentation(line.text as string)
|
|
515
518
|
if (lineIndent > groupIndent) {
|
|
516
519
|
// Do not count synonyms as parents (unless this is correcting a typo in the synonym)
|
|
517
|
-
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted' || /^( {2})+> /.test(line)) {
|
|
520
|
+
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted' || /^( {2})+> /.test(line.text as string)) {
|
|
518
521
|
parents.push(previousId)
|
|
519
522
|
} else {
|
|
520
523
|
parents.push(null)
|
|
@@ -533,10 +536,19 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
533
536
|
groupIndent = lineIndent
|
|
534
537
|
}
|
|
535
538
|
|
|
539
|
+
// Do not process "indet" lines further, as they only serve to indicate
|
|
540
|
+
// that subtaxa are explicitely omitted
|
|
541
|
+
if (isIndetLine(line.text as string, lineIndent)) {
|
|
542
|
+
// If the line was previously not and ndet line, increase the id counter
|
|
543
|
+
if (line.type === ResourceDiffType.Modified && !isIndetLine(line.original as string)) {
|
|
544
|
+
id++
|
|
545
|
+
}
|
|
546
|
+
continue
|
|
547
|
+
}
|
|
548
|
+
|
|
536
549
|
const parentId = parents.reduce((grandparent, parent) => parent || grandparent, null)
|
|
537
550
|
const parent = parentId === null ? {} as WorkingTaxon : data[parentId]
|
|
538
|
-
|
|
539
|
-
const name = line.slice(groupIndent)
|
|
551
|
+
const name = (line.text as string).slice(groupIndent)
|
|
540
552
|
const rank = resource.metadata.levels[groupIndent / 2]
|
|
541
553
|
const item = parseName(name, rank, parent)
|
|
542
554
|
const isSynonym = item.taxonomicStatus !== 'accepted'
|
|
@@ -581,7 +593,10 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
581
593
|
}
|
|
582
594
|
|
|
583
595
|
// Set identifiers
|
|
584
|
-
if (type === ResourceDiffType.Added) {
|
|
596
|
+
if (line.type === ResourceDiffType.Added) {
|
|
597
|
+
newIdOffset++
|
|
598
|
+
item.scientificNameID = idBase + newIdOffset.toString()
|
|
599
|
+
} else if (line.type === ResourceDiffType.Modified && isIndetLine(line.original as string)) {
|
|
585
600
|
newIdOffset++
|
|
586
601
|
item.scientificNameID = idBase + newIdOffset.toString()
|
|
587
602
|
} else {
|
package/test/resources.js
CHANGED
|
@@ -26,9 +26,9 @@ levels: [family, genus, species]
|
|
|
26
26
|
|
|
27
27
|
Dolichurus haemorrhous A. Costa, 1886
|
|
28
28
|
Dolichurus A. Costa, 1886
|
|
29
|
-
|
|
29
|
+
[indet]
|
|
30
30
|
Sphecidae A. Costa, 1886
|
|
31
|
-
|
|
31
|
+
[indet]
|
|
32
32
|
`, 'T1')
|
|
33
33
|
assert.deepStrictEqual(Object.values(resource.taxa).map(taxon => taxon.scientificNameAuthorship), Array(3).fill('A. Costa, 1886'))
|
|
34
34
|
})
|
|
@@ -86,7 +86,7 @@ levels: [genus, species]
|
|
|
86
86
|
---
|
|
87
87
|
|
|
88
88
|
Drymus
|
|
89
|
-
|
|
89
|
+
[indet]
|
|
90
90
|
`, 'T1')
|
|
91
91
|
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Drymus')
|
|
92
92
|
})
|
|
@@ -156,7 +156,7 @@ levels: [genus, species]
|
|
|
156
156
|
---
|
|
157
157
|
|
|
158
158
|
x Triticosecale
|
|
159
|
-
|
|
159
|
+
[indet]
|
|
160
160
|
x Festulpia
|
|
161
161
|
Festuca_rubra x Vulpia_bromoides
|
|
162
162
|
`, 'T1')
|