@larsgw/formica 0.6.1 → 0.6.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 +14 -0
- package/lib/bin/clean-links.d.ts +2 -0
- package/lib/bin/clean-links.js +170 -0
- package/lib/catalog/entities.js +1 -1
- package/lib/catalog/index.d.ts +2 -1
- package/lib/catalog/index.js +2 -1
- package/package.json +3 -2
- package/src/bin/clean-links.ts +96 -0
- package/src/catalog/entities.ts +1 -1
- package/src/catalog/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [0.6.2](https://github.com/identification-resources/formica/compare/v0.6.1...v0.6.2) (2024-04-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **catalog:** include all fields in tabular export ([c4e47bb](https://github.com/identification-resources/formica/commit/c4e47bbdf31c8f589e35c897c8207eb4dd979461))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **catalog:** add script to de-duplicate links ([b719348](https://github.com/identification-resources/formica/commit/b719348635e1b35ee80758c0cdd9938625d79ebf))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
1
15
|
## [0.6.1](https://github.com/identification-resources/formica/compare/v0.6.0...v0.6.1) (2024-03-20)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
13
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
14
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
15
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
16
|
+
function step(op) {
|
|
17
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
18
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
19
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
20
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
21
|
+
switch (op[0]) {
|
|
22
|
+
case 0: case 1: t = op; break;
|
|
23
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
24
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
25
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
26
|
+
default:
|
|
27
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
28
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
29
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
30
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
31
|
+
if (t[2]) _.ops.pop();
|
|
32
|
+
_.trys.pop(); continue;
|
|
33
|
+
}
|
|
34
|
+
op = body.call(thisArg, _);
|
|
35
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
36
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
var fs_1 = require("fs");
|
|
41
|
+
var path = require("path");
|
|
42
|
+
var util = require("util");
|
|
43
|
+
var index_1 = require("../index");
|
|
44
|
+
var LINKS = {
|
|
45
|
+
author: { sheet: 'authors', cleanField: 'main_full_name' },
|
|
46
|
+
publisher: { sheet: 'publishers', cleanField: 'full_name' },
|
|
47
|
+
};
|
|
48
|
+
function replaceLink(value, data, cleanField) {
|
|
49
|
+
var _a;
|
|
50
|
+
var entity = data.get(value);
|
|
51
|
+
return (_a = entity.get(cleanField)) !== null && _a !== void 0 ? _a : value;
|
|
52
|
+
}
|
|
53
|
+
function save(sheet, path) {
|
|
54
|
+
return fs_1.promises.writeFile(path, index_1.csv.formatCsv(sheet.toTable()));
|
|
55
|
+
}
|
|
56
|
+
function main() {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
58
|
+
var args, files, _i, _a, arg, filePath, file, sheet, _loop_1, _b, _c, _d, _e, field;
|
|
59
|
+
return __generator(this, function (_f) {
|
|
60
|
+
switch (_f.label) {
|
|
61
|
+
case 0:
|
|
62
|
+
args = util.parseArgs({ allowPositionals: true });
|
|
63
|
+
files = {};
|
|
64
|
+
_i = 0, _a = args.positionals;
|
|
65
|
+
_f.label = 1;
|
|
66
|
+
case 1:
|
|
67
|
+
if (!(_i < _a.length)) return [3 /*break*/, 4];
|
|
68
|
+
arg = _a[_i];
|
|
69
|
+
filePath = path.resolve(arg);
|
|
70
|
+
return [4 /*yield*/, fs_1.promises.readFile(filePath, 'utf8')];
|
|
71
|
+
case 2:
|
|
72
|
+
file = _f.sent();
|
|
73
|
+
sheet = path.basename(filePath, '.csv');
|
|
74
|
+
files[sheet] = {
|
|
75
|
+
path: filePath,
|
|
76
|
+
sheet: index_1.catalog.loadData(file, sheet),
|
|
77
|
+
};
|
|
78
|
+
_f.label = 3;
|
|
79
|
+
case 3:
|
|
80
|
+
_i++;
|
|
81
|
+
return [3 /*break*/, 1];
|
|
82
|
+
case 4:
|
|
83
|
+
if (!files.catalog) {
|
|
84
|
+
throw new Error('Catalog file must be provided');
|
|
85
|
+
}
|
|
86
|
+
_loop_1 = function (field) {
|
|
87
|
+
var _g, sheet, cleanField, indexField, used, _h, _j, work, value, entities, _k, _l, entity, oldKey, newKey;
|
|
88
|
+
return __generator(this, function (_m) {
|
|
89
|
+
switch (_m.label) {
|
|
90
|
+
case 0:
|
|
91
|
+
_g = LINKS[field], sheet = _g.sheet, cleanField = _g.cleanField;
|
|
92
|
+
indexField = index_1.catalog.getTypeInfo(sheet)[1];
|
|
93
|
+
used = new Set();
|
|
94
|
+
if (!files[sheet]) {
|
|
95
|
+
return [2 /*return*/, "continue"];
|
|
96
|
+
}
|
|
97
|
+
// Update works
|
|
98
|
+
for (_h = 0, _j = files.catalog.sheet.entities; _h < _j.length; _h++) {
|
|
99
|
+
work = _j[_h];
|
|
100
|
+
value = work.get(field);
|
|
101
|
+
if (value === undefined) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
else if (Array.isArray(value)) {
|
|
105
|
+
work.fields[field] = value.map(function (value) {
|
|
106
|
+
value = replaceLink(value, files[sheet].sheet, cleanField);
|
|
107
|
+
used.add(value);
|
|
108
|
+
return value;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
work.fields[field] = replaceLink(value, files[sheet].sheet, cleanField);
|
|
113
|
+
used.add(work.fields[field]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
entities = [];
|
|
117
|
+
for (_k = 0, _l = files[sheet].sheet.entities; _k < _l.length; _k++) {
|
|
118
|
+
entity = _l[_k];
|
|
119
|
+
oldKey = entity.get(indexField);
|
|
120
|
+
if (entity.has(cleanField)) {
|
|
121
|
+
entity.fields[indexField] = entity.get(cleanField);
|
|
122
|
+
delete entity.fields[cleanField];
|
|
123
|
+
}
|
|
124
|
+
newKey = entity.get(indexField);
|
|
125
|
+
if (used.has(newKey) && (newKey === oldKey || !files[sheet].sheet.index[newKey])) {
|
|
126
|
+
entities.push(entity);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Save new file
|
|
130
|
+
return [4 /*yield*/, save(new index_1.catalog.Entities(entities, indexField), files[sheet].path)];
|
|
131
|
+
case 1:
|
|
132
|
+
// Save new file
|
|
133
|
+
_m.sent();
|
|
134
|
+
return [2 /*return*/];
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
_b = LINKS;
|
|
139
|
+
_c = [];
|
|
140
|
+
for (_d in _b)
|
|
141
|
+
_c.push(_d);
|
|
142
|
+
_e = 0;
|
|
143
|
+
_f.label = 5;
|
|
144
|
+
case 5:
|
|
145
|
+
if (!(_e < _c.length)) return [3 /*break*/, 8];
|
|
146
|
+
_d = _c[_e];
|
|
147
|
+
if (!(_d in _b)) return [3 /*break*/, 7];
|
|
148
|
+
field = _d;
|
|
149
|
+
return [5 /*yield**/, _loop_1(field)];
|
|
150
|
+
case 6:
|
|
151
|
+
_f.sent();
|
|
152
|
+
_f.label = 7;
|
|
153
|
+
case 7:
|
|
154
|
+
_e++;
|
|
155
|
+
return [3 /*break*/, 5];
|
|
156
|
+
case 8:
|
|
157
|
+
// Save catalog
|
|
158
|
+
return [4 /*yield*/, save(files.catalog.sheet, files.catalog.path)];
|
|
159
|
+
case 9:
|
|
160
|
+
// Save catalog
|
|
161
|
+
_f.sent();
|
|
162
|
+
return [2 /*return*/];
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
main().catch(function (error) {
|
|
168
|
+
console.error(error);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
});
|
package/lib/catalog/entities.js
CHANGED
|
@@ -97,7 +97,7 @@ var Entities = /** @class */ (function () {
|
|
|
97
97
|
if (this.entities.length === 0) {
|
|
98
98
|
return [[]];
|
|
99
99
|
}
|
|
100
|
-
var header = Object.keys(this.entities[0].
|
|
100
|
+
var header = Object.keys(this.entities[0].schema);
|
|
101
101
|
var table = this.entities.map(function (entity) {
|
|
102
102
|
return header.map(function (field) {
|
|
103
103
|
var value = entity.get(field) || '';
|
package/lib/catalog/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Entities } from './entities';
|
|
2
2
|
import { Entity } from './entity';
|
|
3
|
-
|
|
3
|
+
declare function getTypeInfo(type: string): [typeof Entity, string];
|
|
4
|
+
export { Entities, Entity, getTypeInfo };
|
|
4
5
|
export declare function loadData(file: string, type: string): Entities;
|
package/lib/catalog/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadData = exports.Entity = exports.Entities = void 0;
|
|
3
|
+
exports.loadData = exports.getTypeInfo = exports.Entity = exports.Entities = void 0;
|
|
4
4
|
var entities_1 = require("./entities");
|
|
5
5
|
Object.defineProperty(exports, "Entities", { enumerable: true, get: function () { return entities_1.Entities; } });
|
|
6
6
|
var entity_1 = require("./entity");
|
|
@@ -16,6 +16,7 @@ function getTypeInfo(type) {
|
|
|
16
16
|
default: throw new TypeError("Unknown type \"".concat(type, "\""));
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
exports.getTypeInfo = getTypeInfo;
|
|
19
20
|
function loadData(file, type) {
|
|
20
21
|
var _a = getTypeInfo(type), subClass = _a[0], indexField = _a[1];
|
|
21
22
|
var entities = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larsgw/formica",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.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",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"loir-validate-catalog": "./lib/bin/validate-catalog.js",
|
|
9
9
|
"loir-validate-resources": "./lib/bin/validate-resources-text.js",
|
|
10
10
|
"loir-resources-process": "./lib/bin/process-resources.js",
|
|
11
|
-
"loir-resources-index": "./lib/bin/process-resources-index.js"
|
|
11
|
+
"loir-resources-index": "./lib/bin/process-resources-index.js",
|
|
12
|
+
"loir-catalog-clean-links": "./lib/bin/clean-links.js"
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"test": "node --test --test-reporter spec",
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
})
|
package/src/catalog/entities.ts
CHANGED
|
@@ -50,7 +50,7 @@ export class Entities {
|
|
|
50
50
|
if (this.entities.length === 0) {
|
|
51
51
|
return [[]]
|
|
52
52
|
}
|
|
53
|
-
const header = Object.keys(this.entities[0].
|
|
53
|
+
const header = Object.keys(this.entities[0].schema)
|
|
54
54
|
const table = this.entities.map((entity: Entity) => {
|
|
55
55
|
return header.map((field: string): string => {
|
|
56
56
|
const value = entity.get(field) || ''
|
package/src/catalog/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ function getTypeInfo (type: string): [typeof Entity, string] {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export { Entities, Entity }
|
|
16
|
+
export { Entities, Entity, getTypeInfo }
|
|
17
17
|
|
|
18
18
|
export function loadData (file: string, type: string): Entities {
|
|
19
19
|
const [subClass, indexField] = getTypeInfo(type)
|