@larsgw/formica 0.7.2 → 0.8.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 +23 -0
- package/eslint.config.js +34 -0
- package/lib/bin/SHEETS.js +0 -0
- package/lib/bin/generate-linked-data.d.ts +2 -0
- package/lib/bin/generate-linked-data.js +740 -0
- package/lib/bin/process-resources-index.js +36 -3
- package/lib/bin/process-resources.js +40 -7
- package/lib/bin/util.js +40 -8
- package/lib/bin/validate-catalog.js +36 -3
- package/lib/bin/validate-resources-text.js +36 -3
- package/lib/catalog/entities.js +2 -2
- package/lib/catalog/entity.js +0 -2
- package/lib/catalog/index.js +3 -3
- package/lib/catalog/tables/author.js +5 -3
- package/lib/catalog/tables/index.js +4 -4
- package/lib/catalog/tables/place.js +3 -1
- package/lib/catalog/tables/publisher.js +5 -3
- package/lib/catalog/tables/taxon.js +3 -1
- package/lib/catalog/tables/work.js +5 -5
- package/lib/catalog/value.d.ts +6 -2
- package/lib/catalog/value.js +14 -9
- package/lib/csv.js +2 -3
- package/lib/index.js +37 -4
- package/lib/resources/diff-resource.js +5 -4
- package/lib/resources/parse-text.js +45 -12
- package/lib/taxon-names/index.js +2 -3
- package/package.json +13 -7
- package/src/bin/generate-linked-data.ts +709 -0
- package/src/bin/process-resources-index.ts +1 -1
- package/src/catalog/entity.ts +0 -2
- package/src/catalog/tables/author.ts +5 -3
- package/src/catalog/tables/index.ts +4 -4
- package/src/catalog/tables/place.ts +3 -1
- package/src/catalog/tables/publisher.ts +5 -3
- package/src/catalog/tables/taxon.ts +3 -1
- package/src/catalog/tables/work.ts +5 -5
- package/src/catalog/value.ts +7 -5
- package/src/module.d.ts +5 -0
- package/src/resources/diff-resource.ts +2 -1
- package/src/resources/parse-text.ts +1 -0
- package/tsconfig.json +2 -1
- package/.eslintrc.js +0 -16
- package/src/bin/clean-links.ts +0 -96
|
@@ -39,7 +39,7 @@ async function main (args: string[]): Promise<void> {
|
|
|
39
39
|
|
|
40
40
|
const gbifIndex: Record<string, TaxonId[]> = {}
|
|
41
41
|
const colIndex: Record<string, TaxonId[]> = {}
|
|
42
|
-
const resourceIndex: Record<
|
|
42
|
+
const resourceIndex: Record<ResourceId, AmendedResourceMetadata> = {}
|
|
43
43
|
|
|
44
44
|
await Promise.all(files.map(async function (fileName) {
|
|
45
45
|
if (!fileName.endsWith('.txt')) { return }
|
package/src/catalog/entity.ts
CHANGED
|
@@ -18,10 +18,8 @@ export class Entity {
|
|
|
18
18
|
this.derivedFields = {}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
22
21
|
deriveFields () {
|
|
23
22
|
}
|
|
24
|
-
/* eslint-enable @typescript-eslint/no-empty-function */
|
|
25
23
|
|
|
26
24
|
get (key: string): Value | undefined {
|
|
27
25
|
if (key in this.fields) {
|
|
@@ -4,10 +4,12 @@ import { Entity } from '../entity'
|
|
|
4
4
|
export class Author extends Entity {
|
|
5
5
|
constructor (values: Record<string, string>) {
|
|
6
6
|
super(values, {
|
|
7
|
-
name: { required: true, multiple:
|
|
7
|
+
name: { required: true, multiple: true },
|
|
8
|
+
id: { required: true, multiple: false, format: FORMATS.AUTHOR_ID },
|
|
8
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
9
|
-
|
|
10
|
-
full_names: { required: false, multiple: true }
|
|
10
|
+
display_name: { required: false, multiple: false },
|
|
11
|
+
full_names: { required: false, multiple: true },
|
|
12
|
+
duplicate_of: { required: false, multiple: false, format: FORMATS.AUTHOR_ID }
|
|
11
13
|
})
|
|
12
14
|
}
|
|
13
15
|
}
|
|
@@ -6,9 +6,9 @@ import { Taxon } from './taxon'
|
|
|
6
6
|
import { Work } from './work'
|
|
7
7
|
|
|
8
8
|
export const TYPE_INFO: Record<string, [typeof Entity, string]> = {
|
|
9
|
-
authors: [Author, '
|
|
10
|
-
publishers: [Publisher, '
|
|
11
|
-
places: [Place, '
|
|
12
|
-
taxa: [Taxon, '
|
|
9
|
+
authors: [Author, 'id'],
|
|
10
|
+
publishers: [Publisher, 'id'],
|
|
11
|
+
places: [Place, 'id'],
|
|
12
|
+
taxa: [Taxon, 'id'],
|
|
13
13
|
catalog: [Work, 'id']
|
|
14
14
|
}
|
|
@@ -5,8 +5,10 @@ export class Place extends Entity {
|
|
|
5
5
|
constructor (values: Record<string, string>) {
|
|
6
6
|
super(values, {
|
|
7
7
|
name: { required: true, multiple: false },
|
|
8
|
+
id: { required: true, multiple: false, format: FORMATS.PLACE_ID },
|
|
8
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
9
|
-
display_name: { required: false, multiple: false }
|
|
10
|
+
display_name: { required: false, multiple: false },
|
|
11
|
+
duplicate_of: { required: false, multiple: false, format: FORMATS.PLACE_ID }
|
|
10
12
|
})
|
|
11
13
|
}
|
|
12
14
|
}
|
|
@@ -4,10 +4,12 @@ import { Entity } from '../entity'
|
|
|
4
4
|
export class Publisher extends Entity {
|
|
5
5
|
constructor (values: Record<string, string>) {
|
|
6
6
|
super(values, {
|
|
7
|
-
name: { required: true, multiple:
|
|
7
|
+
name: { required: true, multiple: true },
|
|
8
|
+
id: { required: true, multiple: false, format: FORMATS.PUBLISHER_ID },
|
|
8
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
display_name: { required: false, multiple: false },
|
|
11
|
+
full_names: { required: false, multiple: true },
|
|
12
|
+
duplicate_of: { required: false, multiple: false, format: FORMATS.PUBLISHER_ID }
|
|
11
13
|
})
|
|
12
14
|
}
|
|
13
15
|
}
|
|
@@ -5,11 +5,13 @@ export class Taxon extends Entity {
|
|
|
5
5
|
constructor (values: Record<string, string>) {
|
|
6
6
|
super(values, {
|
|
7
7
|
name: { required: true, multiple: false },
|
|
8
|
+
id: { required: true, multiple: false, format: FORMATS.TAXON_ID },
|
|
8
9
|
qid: { required: false, multiple: false, format: FORMATS.QID },
|
|
9
10
|
rank: { required: false, multiple: false },
|
|
10
11
|
gbif: { required: false, multiple: false, format: FORMATS.INTEGER },
|
|
11
12
|
children_gbif: { required: false, multiple: true, format: FORMATS.INTEGER },
|
|
12
|
-
ancestors_gbif: { required: false, multiple: true, format: FORMATS.INTEGER }
|
|
13
|
+
ancestors_gbif: { required: false, multiple: true, format: FORMATS.INTEGER },
|
|
14
|
+
duplicate_of: { required: false, multiple: false, format: FORMATS.TAXON_ID }
|
|
13
15
|
})
|
|
14
16
|
}
|
|
15
17
|
}
|
|
@@ -4,7 +4,7 @@ import { Entity } from '../entity'
|
|
|
4
4
|
export class Work extends Entity {
|
|
5
5
|
constructor (values: Record<string, string>) {
|
|
6
6
|
super(values, {
|
|
7
|
-
id: { required: true, multiple: false, format: FORMATS.
|
|
7
|
+
id: { required: true, multiple: false, format: FORMATS.WORK_ID },
|
|
8
8
|
title: { required: true, multiple: CHECK.MULTILANG },
|
|
9
9
|
author: { required: false, multiple: true },
|
|
10
10
|
url: { required: false, multiple: true, format: FORMATS.URL },
|
|
@@ -31,10 +31,10 @@ export class Work extends Entity {
|
|
|
31
31
|
region: { required: true, multiple: true },
|
|
32
32
|
complete: { required: false, multiple: false, format: FORMATS.COMPLETE },
|
|
33
33
|
target_taxa: { required: false, multiple: true },
|
|
34
|
-
listed_in: { required: false, multiple: true, format: FORMATS.
|
|
35
|
-
part_of: { required: false, multiple: true, format: FORMATS.
|
|
36
|
-
version_of: { required: false, multiple: true, format: FORMATS.
|
|
37
|
-
duplicate_of: { required: false, multiple: false, format: FORMATS.
|
|
34
|
+
listed_in: { required: false, multiple: true, format: FORMATS.WORK_ID },
|
|
35
|
+
part_of: { required: false, multiple: true, format: FORMATS.WORK_ID },
|
|
36
|
+
version_of: { required: false, multiple: true, format: FORMATS.WORK_ID },
|
|
37
|
+
duplicate_of: { required: false, multiple: false, format: FORMATS.WORK_ID }
|
|
38
38
|
})
|
|
39
39
|
}
|
|
40
40
|
|
package/src/catalog/value.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import spdxLicenseList
|
|
2
|
-
|
|
3
|
-
// @ts-ignore
|
|
4
|
-
import ietfTagListFactory = require('ietf-language-tag-regex')
|
|
1
|
+
import spdxLicenseList from 'spdx-license-list'
|
|
2
|
+
import ietfTagListFactory from 'ietf-language-tag-regex'
|
|
5
3
|
|
|
6
4
|
const ietfTagList = ietfTagListFactory()
|
|
7
5
|
|
|
@@ -10,7 +8,11 @@ export const FORMATS = {
|
|
|
10
8
|
KEY_TYPE: ['key', 'matrix', 'reference', 'gallery', 'checklist', 'supplement', 'collection', 'algorithm'],
|
|
11
9
|
COMPLETE: ['TRUE', 'FALSE'],
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
WORK_ID: /^B[1-9]\d*$/,
|
|
12
|
+
AUTHOR_ID: /^A[1-9]\d*$/,
|
|
13
|
+
PLACE_ID: /^G[1-9]\d*$/,
|
|
14
|
+
PUBLISHER_ID: /^P[1-9]\d*$/,
|
|
15
|
+
TAXON_ID: /^T[1-9]\d*$/,
|
|
14
16
|
EDTF_0: /^(\d{4}(-\d{2}(-\d{2}(T\d{2}:\d{2}:\d{2}(Z|[-+]\d{2}(:\d{2})?))?)?)?|\d{4}(-\d{2}(-\d{2})?)?\/(\d{4}(-\d{2}(-\d{2})?)?|\.\.))$/,
|
|
15
17
|
ISSN_L: /^[0-9]{4}-[0-9]{3}[0-9X]$/,
|
|
16
18
|
ISBN: /^(\d{13}|\d{9}[0-9X])$/,
|
package/src/module.d.ts
CHANGED
|
@@ -294,6 +294,7 @@ function parseName (name: string, rank: Rank, parent: WorkingTaxon): WorkingTaxo
|
|
|
294
294
|
item.taxonRemarks = notes
|
|
295
295
|
item.taxonRank = rank
|
|
296
296
|
|
|
297
|
+
// @ts-expect-error TS1501: This regular expression flag is only available when targeting 'es6' or later.
|
|
297
298
|
if (/[^\p{L}0-9\u{00D7}\- ]/u.test(taxon)) {
|
|
298
299
|
throw new Error(`Taxon name contains unexpected characters: "${taxon}"`)
|
|
299
300
|
}
|
package/tsconfig.json
CHANGED
package/.eslintrc.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
|
3
|
-
parser: '@typescript-eslint/parser',
|
|
4
|
-
plugins: ['@typescript-eslint'],
|
|
5
|
-
root: true,
|
|
6
|
-
|
|
7
|
-
rules: {
|
|
8
|
-
indent: ['error', 4, { SwitchCase: 1 }],
|
|
9
|
-
'no-unused-vars': 'off',
|
|
10
|
-
'@typescript-eslint/no-unused-vars': ['warn', {
|
|
11
|
-
argsIgnorePattern: '^_',
|
|
12
|
-
varsIgnorePattern: '^_',
|
|
13
|
-
caughtErrorsIgnorePattern: '^_'
|
|
14
|
-
}]
|
|
15
|
-
}
|
|
16
|
-
}
|
package/src/bin/clean-links.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { promises as fs } from 'fs'
|
|
4
|
-
import * as path from 'path'
|
|
5
|
-
import * as util from 'util'
|
|
6
|
-
|
|
7
|
-
import { catalog, csv } from '../index'
|
|
8
|
-
|
|
9
|
-
const LINKS: Record<string, { sheet: string, cleanField: string }> = {
|
|
10
|
-
author: { sheet: 'authors', cleanField: 'main_full_name' },
|
|
11
|
-
publisher: { sheet: 'publishers', cleanField: 'full_name' },
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function replaceLink (value: string, data: catalog.Entities, cleanField: string): string {
|
|
15
|
-
const entity = data.get(value)
|
|
16
|
-
return entity.get(cleanField) as string ?? value
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function save (sheet: catalog.Entities, path: string): Promise<void> {
|
|
20
|
-
return fs.writeFile(path, csv.formatCsv(sheet.toTable()))
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function main (): Promise<void> {
|
|
24
|
-
const args = util.parseArgs({ allowPositionals: true })
|
|
25
|
-
|
|
26
|
-
const files: Record<string, { path: string, sheet: catalog.Entities }> = {}
|
|
27
|
-
for (const arg of args.positionals) {
|
|
28
|
-
const filePath = path.resolve(arg)
|
|
29
|
-
const file = await fs.readFile(filePath, 'utf8')
|
|
30
|
-
const sheet = path.basename(filePath, '.csv')
|
|
31
|
-
files[sheet] = {
|
|
32
|
-
path: filePath,
|
|
33
|
-
sheet: catalog.loadData(file, sheet),
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!files.catalog) {
|
|
38
|
-
throw new Error('Catalog file must be provided')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
for (const field in LINKS) {
|
|
42
|
-
const { sheet, cleanField } = LINKS[field]
|
|
43
|
-
const indexField = catalog.getTypeInfo(sheet)[1]
|
|
44
|
-
const used = new Set()
|
|
45
|
-
|
|
46
|
-
if (!files[sheet]) {
|
|
47
|
-
continue
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Update works
|
|
51
|
-
for (const work of files.catalog.sheet.entities) {
|
|
52
|
-
const value = work.get(field)
|
|
53
|
-
|
|
54
|
-
if (value === undefined) {
|
|
55
|
-
continue
|
|
56
|
-
} else if (Array.isArray(value)) {
|
|
57
|
-
work.fields[field] = value.map(value => {
|
|
58
|
-
value = replaceLink(value, files[sheet].sheet, cleanField)
|
|
59
|
-
used.add(value)
|
|
60
|
-
return value
|
|
61
|
-
})
|
|
62
|
-
} else {
|
|
63
|
-
work.fields[field] = replaceLink(value, files[sheet].sheet, cleanField)
|
|
64
|
-
used.add(work.fields[field])
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Filter unused values
|
|
69
|
-
const entities = []
|
|
70
|
-
for (const entity of files[sheet].sheet.entities) {
|
|
71
|
-
const oldKey = entity.get(indexField) as string
|
|
72
|
-
|
|
73
|
-
if (entity.has(cleanField)) {
|
|
74
|
-
entity.fields[indexField] = entity.get(cleanField) as Value
|
|
75
|
-
delete entity.fields[cleanField]
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const newKey = entity.get(indexField) as string
|
|
79
|
-
|
|
80
|
-
if (used.has(newKey) && (newKey === oldKey || !files[sheet].sheet.index[newKey])) {
|
|
81
|
-
entities.push(entity)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Save new file
|
|
86
|
-
await save(new catalog.Entities(entities, indexField), files[sheet].path)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Save catalog
|
|
90
|
-
await save(files.catalog.sheet, files.catalog.path)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
main().catch((error: Error) => {
|
|
94
|
-
console.error(error)
|
|
95
|
-
process.exit(1)
|
|
96
|
-
})
|