@larsgw/formica 0.3.0 → 0.4.0
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 +26 -0
- package/lib/catalog/entity.js +17 -2
- package/lib/catalog/tables/work.js +2 -1
- package/lib/resources/parse-text.js +3 -3
- package/package.json +1 -1
- package/src/catalog/entity.ts +7 -2
- package/src/catalog/tables/work.ts +2 -1
- package/src/resources/parse-text.ts +3 -3
- package/test/resources.js +43 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
# [0.4.0](https://github.com/identification-resources/formica/compare/v0.3.1...v0.4.0) (2023-09-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **catalog:** validation checks for unknown fields ([5fe5cf0](https://github.com/identification-resources/formica/commit/5fe5cf0b8cf4db7c4af50a897821077e410a1e09))
|
|
7
|
+
* **catalog:** verify if required fields are missing ([c327ae4](https://github.com/identification-resources/formica/commit/c327ae458a80567aac34dd03f3970dcf9d575479))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **catalog:** add duplicate_of field ([e4a8bbe](https://github.com/identification-resources/formica/commit/e4a8bbecef9c6a40d0f2cc5d4e8bb67685a453ff))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [0.3.1](https://github.com/identification-resources/formica/compare/v0.3.0...v0.3.1) (2023-09-04)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **resources:** allow correction of synonyms of lowest taxa ([c737d5b](https://github.com/identification-resources/formica/commit/c737d5b09d5e5b24f4cc2c8cff88c9ada8d0ab7b))
|
|
22
|
+
* **resources:** fix regression in c737d5b ([d4e52e9](https://github.com/identification-resources/formica/commit/d4e52e95ed1a35dabbd80b9f9aace1a824fdae98))
|
|
23
|
+
* **resources:** handle corrections to synonyms ([21ed9b7](https://github.com/identification-resources/formica/commit/21ed9b79f77e1e6d0639d96ed9822d5212a85a2e)), closes [#4](https://github.com/identification-resources/formica/issues/4)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
1
27
|
# [0.3.0](https://github.com/identification-resources/formica/compare/v0.2.1...v0.3.0) (2023-08-20)
|
|
2
28
|
|
|
3
29
|
|
package/lib/catalog/entity.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.Entity = void 0;
|
|
4
15
|
var value_1 = require("./value");
|
|
@@ -6,7 +17,7 @@ var Entity = /** @class */ (function () {
|
|
|
6
17
|
function Entity(values, schema) {
|
|
7
18
|
this.fields = {};
|
|
8
19
|
for (var key in values) {
|
|
9
|
-
var value = (0, value_1.parseValue)(values[key], !!schema[key].multiple);
|
|
20
|
+
var value = (0, value_1.parseValue)(values[key], !!schema[key] && !!schema[key].multiple);
|
|
10
21
|
if (value !== null) {
|
|
11
22
|
this.fields[key] = value;
|
|
12
23
|
}
|
|
@@ -31,7 +42,7 @@ var Entity = /** @class */ (function () {
|
|
|
31
42
|
};
|
|
32
43
|
Entity.prototype.validate = function () {
|
|
33
44
|
var errors = [];
|
|
34
|
-
for (var field in this.fields) {
|
|
45
|
+
for (var field in __assign(__assign({}, this.fields), this.schema)) {
|
|
35
46
|
for (var _i = 0, _a = this._validateField(field); _i < _a.length; _i++) {
|
|
36
47
|
var error = _a[_i];
|
|
37
48
|
errors.push({ field: field, error: error });
|
|
@@ -41,6 +52,10 @@ var Entity = /** @class */ (function () {
|
|
|
41
52
|
};
|
|
42
53
|
Entity.prototype._validateField = function (field) {
|
|
43
54
|
var errors = [];
|
|
55
|
+
if (!(field in this.schema)) {
|
|
56
|
+
errors.push('Field unknown');
|
|
57
|
+
return errors;
|
|
58
|
+
}
|
|
44
59
|
if (!(field in this.fields)) {
|
|
45
60
|
if (this.schema[field].required) {
|
|
46
61
|
errors.push('Value(s) required but missing');
|
|
@@ -51,7 +51,8 @@ var Work = /** @class */ (function (_super) {
|
|
|
51
51
|
target_taxa: { required: false, multiple: true },
|
|
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
|
-
version_of: { required: false, multiple: true, format: value_1.FORMATS.ID }
|
|
54
|
+
version_of: { required: false, multiple: true, format: value_1.FORMATS.ID },
|
|
55
|
+
// duplicate_of: { required: false, multiple: false, format: FORMATS.ID }
|
|
55
56
|
}) || this;
|
|
56
57
|
}
|
|
57
58
|
Work.prototype.deriveFields = function () {
|
|
@@ -361,7 +361,7 @@ function parseHeader(header) {
|
|
|
361
361
|
}
|
|
362
362
|
function validateResource(config, content) {
|
|
363
363
|
// Check for too much indentation
|
|
364
|
-
var longerIndent = new RegExp("^( ){".concat(config.levels.length - 1, "}(?! [+=>] ) "), 'm');
|
|
364
|
+
var longerIndent = new RegExp("^( ){".concat(config.levels.length - 1, "}(?! [+=>] | > ) "), 'm');
|
|
365
365
|
var longerIndentMatch = content.match(longerIndent);
|
|
366
366
|
if (longerIndentMatch !== null) {
|
|
367
367
|
var offset = longerIndentMatch.index;
|
|
@@ -402,8 +402,8 @@ function parseResourceContent(content, resource, oldIds) {
|
|
|
402
402
|
}
|
|
403
403
|
var lineIndent = line.match(/^ */)[0].length;
|
|
404
404
|
if (lineIndent > groupIndent) {
|
|
405
|
-
// Do not count synonyms as parents
|
|
406
|
-
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted') {
|
|
405
|
+
// Do not count synonyms as parents (unless this is correcting a typo in the synonym)
|
|
406
|
+
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted' || /^( {2})+> /.test(line)) {
|
|
407
407
|
parents.push(previousId);
|
|
408
408
|
}
|
|
409
409
|
else {
|
package/package.json
CHANGED
package/src/catalog/entity.ts
CHANGED
|
@@ -8,7 +8,7 @@ export class Entity {
|
|
|
8
8
|
constructor (values: Record<string, string>, schema: Schema) {
|
|
9
9
|
this.fields = {}
|
|
10
10
|
for (const key in values) {
|
|
11
|
-
const value = parseValue(values[key], !!schema[key].multiple)
|
|
11
|
+
const value = parseValue(values[key], !!schema[key] && !!schema[key].multiple)
|
|
12
12
|
if (value !== null) {
|
|
13
13
|
this.fields[key] = value
|
|
14
14
|
}
|
|
@@ -37,7 +37,7 @@ export class Entity {
|
|
|
37
37
|
|
|
38
38
|
validate (): FieldError[] {
|
|
39
39
|
const errors = []
|
|
40
|
-
for (const field in this.fields) {
|
|
40
|
+
for (const field in { ...this.fields, ...this.schema }) {
|
|
41
41
|
for (const error of this._validateField(field)) {
|
|
42
42
|
errors.push({ field, error })
|
|
43
43
|
}
|
|
@@ -48,6 +48,11 @@ export class Entity {
|
|
|
48
48
|
_validateField (field: string) {
|
|
49
49
|
const errors = []
|
|
50
50
|
|
|
51
|
+
if (!(field in this.schema)) {
|
|
52
|
+
errors.push('Field unknown')
|
|
53
|
+
return errors
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
if (!(field in this.fields)) {
|
|
52
57
|
if (this.schema[field].required) {
|
|
53
58
|
errors.push('Value(s) required but missing')
|
|
@@ -33,7 +33,8 @@ export class Work extends Entity {
|
|
|
33
33
|
target_taxa: { required: false, multiple: true },
|
|
34
34
|
listed_in: { required: false, multiple: true, format: FORMATS.ID },
|
|
35
35
|
part_of: { required: false, multiple: true, format: FORMATS.ID },
|
|
36
|
-
version_of: { required: false, multiple: true, format: FORMATS.ID }
|
|
36
|
+
version_of: { required: false, multiple: true, format: FORMATS.ID },
|
|
37
|
+
// duplicate_of: { required: false, multiple: false, format: FORMATS.ID }
|
|
37
38
|
})
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -372,7 +372,7 @@ function parseHeader (header: string): ResourceMetadata {
|
|
|
372
372
|
|
|
373
373
|
function validateResource (config: ResourceMetadata, content: string) {
|
|
374
374
|
// Check for too much indentation
|
|
375
|
-
const longerIndent = new RegExp(`^( ){${config.levels.length - 1}}(?! [+=>] ) `, 'm')
|
|
375
|
+
const longerIndent = new RegExp(`^( ){${config.levels.length - 1}}(?! [+=>] | > ) `, 'm')
|
|
376
376
|
const longerIndentMatch = content.match(longerIndent)
|
|
377
377
|
if (longerIndentMatch !== null) {
|
|
378
378
|
const offset = longerIndentMatch.index
|
|
@@ -425,8 +425,8 @@ function parseResourceContent (content: ResourceDiff, resource: Resource, oldIds
|
|
|
425
425
|
const lineIndent = (line.match(/^ */) as string[])[0].length
|
|
426
426
|
|
|
427
427
|
if (lineIndent > groupIndent) {
|
|
428
|
-
// Do not count synonyms as parents
|
|
429
|
-
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted') {
|
|
428
|
+
// Do not count synonyms as parents (unless this is correcting a typo in the synonym)
|
|
429
|
+
if (data[previousId] && data[previousId].taxonomicStatus === 'accepted' || /^( {2})+> /.test(line)) {
|
|
430
430
|
parents.push(previousId)
|
|
431
431
|
} else {
|
|
432
432
|
parents.push(null)
|
package/test/resources.js
CHANGED
|
@@ -1,10 +1,51 @@
|
|
|
1
1
|
const test = require('node:test')
|
|
2
2
|
const assert = require('assert')
|
|
3
3
|
|
|
4
|
-
const { resources } = require('../lib')
|
|
4
|
+
const { catalog, resources } = require('../lib')
|
|
5
|
+
|
|
6
|
+
test('catalog', async (t) => {
|
|
7
|
+
await t.test('reports missing required fields', (t) => {
|
|
8
|
+
const errors = catalog.loadData(`id
|
|
9
|
+
B1`, 'catalog').validate()
|
|
10
|
+
assert.deepStrictEqual(errors, [
|
|
11
|
+
{ entity: 'B1', field: 'title', error: 'Value(s) required but missing' },
|
|
12
|
+
{ entity: 'B1', field: 'entry_type', error: 'Value(s) required but missing' },
|
|
13
|
+
{ entity: 'B1', field: 'language', error: 'Value(s) required but missing' },
|
|
14
|
+
{ entity: 'B1', field: 'key_type', error: 'Value(s) required but missing' },
|
|
15
|
+
{ entity: 'B1', field: 'taxon', error: 'Value(s) required but missing' },
|
|
16
|
+
{ entity: 'B1', field: 'region', error: 'Value(s) required but missing' }
|
|
17
|
+
])
|
|
18
|
+
})
|
|
19
|
+
})
|
|
5
20
|
|
|
6
21
|
test('resources', async (t) => {
|
|
7
|
-
await t.test('
|
|
22
|
+
await t.test('parses author with initials', (t) => {
|
|
23
|
+
const [resource] = resources.parseTextFile(`---
|
|
24
|
+
levels: [family, genus, species]
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
Dolichurus haemorrhous A. Costa, 1886
|
|
28
|
+
Dolichurus A. Costa, 1886
|
|
29
|
+
Dolichurus indet.
|
|
30
|
+
Sphecidae A. Costa, 1886
|
|
31
|
+
Sphecidae indet.
|
|
32
|
+
`, 'T1')
|
|
33
|
+
assert.deepStrictEqual(Object.values(resource.taxa).map(taxon => taxon.scientificNameAuthorship), Array(3).fill('A. Costa, 1886'))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
await t.test('does not validate name with correction', (t) => {
|
|
37
|
+
const [resource] = resources.parseTextFile(`---
|
|
38
|
+
levels: [species]
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
Clytochrysus lapidarius (Panzer, 1804)
|
|
42
|
+
= Crabo chrysostomus Lepeletier & Brullé, 1835
|
|
43
|
+
> Crabro chrysostomus Lepeletier & Brullé, 1835
|
|
44
|
+
`, 'T1')
|
|
45
|
+
assert.strictEqual(resource.taxa['T1:1:1'].scientificName, 'Clytochrysus lapidarius (Panzer, 1804)')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
await t.test('errors for missing leaf taxa', (t) => {
|
|
8
49
|
assert.throws(() => {
|
|
9
50
|
resources.parseTextFile(`---
|
|
10
51
|
levels: [family, genus, species]
|