@willwade/aac-processors 0.0.11 → 0.0.12
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/dist/cli/index.js +7 -0
- package/dist/core/analyze.js +1 -0
- package/dist/core/treeStructure.d.ts +12 -2
- package/dist/core/treeStructure.js +6 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +20 -3
- package/dist/{analytics → optional/analytics}/history.d.ts +3 -3
- package/dist/{analytics → optional/analytics}/history.js +3 -3
- package/dist/optional/analytics/index.d.ts +28 -0
- package/dist/optional/analytics/index.js +73 -0
- package/dist/optional/analytics/metrics/comparison.d.ts +36 -0
- package/dist/optional/analytics/metrics/comparison.js +330 -0
- package/dist/optional/analytics/metrics/core.d.ts +36 -0
- package/dist/optional/analytics/metrics/core.js +422 -0
- package/dist/optional/analytics/metrics/effort.d.ts +137 -0
- package/dist/optional/analytics/metrics/effort.js +198 -0
- package/dist/optional/analytics/metrics/index.d.ts +15 -0
- package/dist/optional/analytics/metrics/index.js +36 -0
- package/dist/optional/analytics/metrics/sentence.d.ts +49 -0
- package/dist/optional/analytics/metrics/sentence.js +112 -0
- package/dist/optional/analytics/metrics/types.d.ts +157 -0
- package/dist/optional/analytics/metrics/types.js +7 -0
- package/dist/optional/analytics/metrics/vocabulary.d.ts +65 -0
- package/dist/optional/analytics/metrics/vocabulary.js +140 -0
- package/dist/optional/analytics/reference/index.d.ts +51 -0
- package/dist/optional/analytics/reference/index.js +102 -0
- package/dist/optional/analytics/utils/idGenerator.d.ts +59 -0
- package/dist/optional/analytics/utils/idGenerator.js +96 -0
- package/dist/processors/gridsetProcessor.js +25 -0
- package/dist/processors/index.d.ts +1 -0
- package/dist/processors/index.js +5 -3
- package/dist/processors/obfProcessor.js +25 -2
- package/dist/processors/obfsetProcessor.d.ts +26 -0
- package/dist/processors/obfsetProcessor.js +179 -0
- package/dist/processors/snapProcessor.js +29 -1
- package/dist/processors/touchchatProcessor.js +27 -0
- package/dist/types/aac.d.ts +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OBF Set Processor - Handles JSON-formatted .obfset files
|
|
4
|
+
* These are pre-extracted board sets in JSON array format
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ObfsetProcessor = void 0;
|
|
11
|
+
const treeStructure_1 = require("../core/treeStructure");
|
|
12
|
+
const treeStructure_2 = require("../core/treeStructure");
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const baseProcessor_1 = require("../core/baseProcessor");
|
|
15
|
+
class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
super(options);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract all text content
|
|
21
|
+
*/
|
|
22
|
+
extractTexts(filePathOrBuffer) {
|
|
23
|
+
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
24
|
+
const texts = new Set();
|
|
25
|
+
Object.values(tree.pages).forEach((page) => {
|
|
26
|
+
if (page.name)
|
|
27
|
+
texts.add(page.name);
|
|
28
|
+
page.buttons.forEach((button) => {
|
|
29
|
+
if (button.label)
|
|
30
|
+
texts.add(button.label);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
return Array.from(texts);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Load an .obfset file (JSON array of boards)
|
|
37
|
+
*/
|
|
38
|
+
loadIntoTree(filePathOrBuffer) {
|
|
39
|
+
const tree = new treeStructure_1.AACTree();
|
|
40
|
+
let content;
|
|
41
|
+
if (Buffer.isBuffer(filePathOrBuffer)) {
|
|
42
|
+
content = filePathOrBuffer.toString('utf-8');
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
content = fs_1.default.readFileSync(filePathOrBuffer, 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
const boards = JSON.parse(content);
|
|
48
|
+
// Track board ID mappings
|
|
49
|
+
const boardMap = new Map();
|
|
50
|
+
// First pass: create all boards
|
|
51
|
+
boards.forEach((boardData) => {
|
|
52
|
+
const rows = boardData.grid?.rows || 4;
|
|
53
|
+
const cols = boardData.grid?.columns || 6;
|
|
54
|
+
const name = boardData.name || boardData.id || `Board ${boardData.id}`;
|
|
55
|
+
const page = new treeStructure_2.AACPage({
|
|
56
|
+
id: boardData.id,
|
|
57
|
+
name,
|
|
58
|
+
grid: { columns: cols, rows: rows },
|
|
59
|
+
buttons: [],
|
|
60
|
+
});
|
|
61
|
+
tree.addPage(page);
|
|
62
|
+
boardMap.set(boardData.id, page);
|
|
63
|
+
});
|
|
64
|
+
// Second pass: process buttons and establish parent relationships
|
|
65
|
+
boards.forEach((boardData) => {
|
|
66
|
+
const page = boardMap.get(boardData.id);
|
|
67
|
+
if (!page)
|
|
68
|
+
return;
|
|
69
|
+
const rows = boardData.grid?.rows || 4;
|
|
70
|
+
const cols = boardData.grid?.columns || 6;
|
|
71
|
+
// Initialize grid with nulls
|
|
72
|
+
page.grid = Array.from({ length: rows }, () => Array.from({ length: cols }, () => null));
|
|
73
|
+
// Create button map by ID
|
|
74
|
+
const buttonMap = new Map();
|
|
75
|
+
const buttons = boardData.buttons || [];
|
|
76
|
+
buttons.forEach((btnData) => {
|
|
77
|
+
buttonMap.set(btnData.id, btnData);
|
|
78
|
+
});
|
|
79
|
+
// Process grid order to place buttons in correct positions
|
|
80
|
+
const gridOrder = boardData.grid?.order || [];
|
|
81
|
+
const semanticIds = [];
|
|
82
|
+
const cloneIds = [];
|
|
83
|
+
gridOrder.forEach((row, rowIndex) => {
|
|
84
|
+
row.forEach((buttonId, colIndex) => {
|
|
85
|
+
const btnData = buttonMap.get(buttonId);
|
|
86
|
+
if (btnData) {
|
|
87
|
+
// Create semantic action
|
|
88
|
+
let semanticAction;
|
|
89
|
+
if (btnData.load_board?.id) {
|
|
90
|
+
// Navigation button
|
|
91
|
+
semanticAction = {
|
|
92
|
+
category: treeStructure_2.AACSemanticCategory.NAVIGATION,
|
|
93
|
+
intent: treeStructure_2.AACSemanticIntent.NAVIGATE_TO,
|
|
94
|
+
targetId: btnData.load_board.id,
|
|
95
|
+
fallback: {
|
|
96
|
+
type: 'NAVIGATE',
|
|
97
|
+
targetPageId: btnData.load_board.id,
|
|
98
|
+
add_to_sentence: btnData.load_board.add_to_sentence,
|
|
99
|
+
temporary_home: btnData.load_board.temporary_home,
|
|
100
|
+
},
|
|
101
|
+
platformData: {
|
|
102
|
+
grid3: {
|
|
103
|
+
commandId: 'GO_TO_BOARD',
|
|
104
|
+
parameters: {
|
|
105
|
+
add_to_sentence: btnData.load_board.add_to_sentence,
|
|
106
|
+
temporary_home: btnData.load_board.temporary_home,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Speaking button
|
|
114
|
+
semanticAction = {
|
|
115
|
+
category: treeStructure_2.AACSemanticCategory.COMMUNICATION,
|
|
116
|
+
intent: treeStructure_2.AACSemanticIntent.SPEAK_TEXT,
|
|
117
|
+
text: btnData.label || '',
|
|
118
|
+
fallback: { type: 'SPEAK', message: btnData.label || '' },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const button = new treeStructure_2.AACButton({
|
|
122
|
+
id: btnData.id,
|
|
123
|
+
label: btnData.label || '',
|
|
124
|
+
message: btnData.label || '',
|
|
125
|
+
targetPageId: btnData.load_board?.id,
|
|
126
|
+
semanticAction,
|
|
127
|
+
semantic_id: btnData.semantic_id,
|
|
128
|
+
clone_id: btnData.clone_id,
|
|
129
|
+
});
|
|
130
|
+
// Add to grid at the correct position
|
|
131
|
+
if (rowIndex < rows && colIndex < cols) {
|
|
132
|
+
page.grid[rowIndex][colIndex] = button;
|
|
133
|
+
}
|
|
134
|
+
page.buttons.push(button);
|
|
135
|
+
// Track IDs
|
|
136
|
+
if (btnData.semantic_id) {
|
|
137
|
+
semanticIds.push(String(btnData.semantic_id));
|
|
138
|
+
}
|
|
139
|
+
if (btnData.clone_id) {
|
|
140
|
+
cloneIds.push(String(btnData.clone_id));
|
|
141
|
+
}
|
|
142
|
+
// Establish parent relationship if this button links to another board
|
|
143
|
+
if (btnData.load_board?.id) {
|
|
144
|
+
const targetPage = boardMap.get(String(btnData.load_board.id));
|
|
145
|
+
if (targetPage) {
|
|
146
|
+
targetPage.parentId = page.id;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
// Store IDs on page
|
|
153
|
+
page.semantic_ids = semanticIds;
|
|
154
|
+
page.clone_ids = cloneIds;
|
|
155
|
+
});
|
|
156
|
+
// Set root board (first board or one with no parent)
|
|
157
|
+
const rootBoard = Array.from(boardMap.values()).find((p) => !p.parentId);
|
|
158
|
+
if (rootBoard) {
|
|
159
|
+
tree.rootId = rootBoard.id;
|
|
160
|
+
}
|
|
161
|
+
return tree;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Process texts (not supported for .obfset currently)
|
|
165
|
+
*/
|
|
166
|
+
processTexts(_filePathOrBuffer, _translations, _outputPath) {
|
|
167
|
+
throw new Error('processTexts is not supported for .obfset currently');
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Save tree structure back to file
|
|
171
|
+
*/
|
|
172
|
+
saveFromTree(_tree, _outputPath) {
|
|
173
|
+
throw new Error('saveFromTree is not supported for .obfset currently');
|
|
174
|
+
}
|
|
175
|
+
supportsExtension(extension) {
|
|
176
|
+
return extension === '.obfset';
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.ObfsetProcessor = ObfsetProcessor;
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.SnapProcessor = void 0;
|
|
7
7
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
|
-
|
|
9
|
+
const idGenerator_1 = require("../optional/analytics/utils/idGenerator");
|
|
10
10
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -226,6 +226,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
226
226
|
targetPageId: targetPageUniqueId,
|
|
227
227
|
semanticAction: semanticAction,
|
|
228
228
|
audioRecording: audioRecording,
|
|
229
|
+
semantic_id: btnRow.LibrarySymbolId
|
|
230
|
+
? `snap_symbol_${btnRow.LibrarySymbolId}`
|
|
231
|
+
: undefined, // Extract semantic_id from LibrarySymbolId
|
|
229
232
|
style: {
|
|
230
233
|
backgroundColor: btnRow.BackgroundColor
|
|
231
234
|
? `#${btnRow.BackgroundColor.toString(16)}`
|
|
@@ -258,6 +261,10 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
258
261
|
gridX < 10 &&
|
|
259
262
|
pageGrid[gridY] &&
|
|
260
263
|
pageGrid[gridY][gridX] === null) {
|
|
264
|
+
// Generate clone_id for button at this position
|
|
265
|
+
const rows = pageGrid.length;
|
|
266
|
+
const cols = pageGrid[0] ? pageGrid[0].length : 10;
|
|
267
|
+
button.clone_id = (0, idGenerator_1.generateCloneId)(rows, cols, gridY, gridX, button.label);
|
|
261
268
|
pageGrid[gridY][gridX] = button;
|
|
262
269
|
}
|
|
263
270
|
}
|
|
@@ -274,6 +281,27 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
274
281
|
const currentPage = tree.getPage(uniqueId);
|
|
275
282
|
if (currentPage && pageGrid) {
|
|
276
283
|
currentPage.grid = pageGrid;
|
|
284
|
+
// Track semantic_ids and clone_ids on the page
|
|
285
|
+
const semanticIds = [];
|
|
286
|
+
const cloneIds = [];
|
|
287
|
+
pageGrid.forEach((row) => {
|
|
288
|
+
row.forEach((btn) => {
|
|
289
|
+
if (btn) {
|
|
290
|
+
if (btn.semantic_id) {
|
|
291
|
+
semanticIds.push(btn.semantic_id);
|
|
292
|
+
}
|
|
293
|
+
if (btn.clone_id) {
|
|
294
|
+
cloneIds.push(btn.clone_id);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
if (semanticIds.length > 0) {
|
|
300
|
+
currentPage.semantic_ids = semanticIds;
|
|
301
|
+
}
|
|
302
|
+
if (cloneIds.length > 0) {
|
|
303
|
+
currentPage.clone_ids = cloneIds;
|
|
304
|
+
}
|
|
277
305
|
}
|
|
278
306
|
}
|
|
279
307
|
return tree;
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.TouchChatProcessor = void 0;
|
|
7
7
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
|
+
const idGenerator_1 = require("../optional/analytics/utils/idGenerator");
|
|
9
10
|
const stringCasing_1 = require("../core/stringCasing");
|
|
10
11
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
11
12
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
@@ -165,6 +166,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
165
166
|
label: cell.label || '',
|
|
166
167
|
message: cell.message || '',
|
|
167
168
|
semanticAction: semanticAction,
|
|
169
|
+
semantic_id: cell.symbol_link_id || cell.symbolLinkId || undefined, // Extract semantic_id from symbol_link_id
|
|
168
170
|
style: {
|
|
169
171
|
backgroundColor: intToHex(style?.body_color),
|
|
170
172
|
borderColor: intToHex(style?.border_color),
|
|
@@ -241,6 +243,31 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
241
243
|
const page = tree.getPage(pageId);
|
|
242
244
|
if (page) {
|
|
243
245
|
page.grid = grid;
|
|
246
|
+
// Generate clone_id for each button in the grid
|
|
247
|
+
const semanticIds = [];
|
|
248
|
+
const cloneIds = [];
|
|
249
|
+
grid.forEach((row, rowIndex) => {
|
|
250
|
+
row.forEach((btn, colIndex) => {
|
|
251
|
+
if (btn) {
|
|
252
|
+
// Generate clone_id based on position and label
|
|
253
|
+
const rows = grid.length;
|
|
254
|
+
const cols = grid[0] ? grid[0].length : 10;
|
|
255
|
+
btn.clone_id = (0, idGenerator_1.generateCloneId)(rows, cols, rowIndex, colIndex, btn.label);
|
|
256
|
+
cloneIds.push(btn.clone_id);
|
|
257
|
+
// Track semantic_id if present
|
|
258
|
+
if (btn.semantic_id) {
|
|
259
|
+
semanticIds.push(btn.semantic_id);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
// Track IDs on the page
|
|
265
|
+
if (semanticIds.length > 0) {
|
|
266
|
+
page.semantic_ids = semanticIds;
|
|
267
|
+
}
|
|
268
|
+
if (cloneIds.length > 0) {
|
|
269
|
+
page.clone_ids = cloneIds;
|
|
270
|
+
}
|
|
244
271
|
}
|
|
245
272
|
});
|
|
246
273
|
}
|
package/dist/types/aac.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface AACButton {
|
|
|
42
42
|
parameters?: {
|
|
43
43
|
[key: string]: any;
|
|
44
44
|
};
|
|
45
|
+
semantic_id?: string;
|
|
46
|
+
clone_id?: string;
|
|
45
47
|
}
|
|
46
48
|
export interface AACPage {
|
|
47
49
|
id: string;
|
|
@@ -54,6 +56,8 @@ export interface AACPage {
|
|
|
54
56
|
descriptionHtml?: string;
|
|
55
57
|
images?: any[];
|
|
56
58
|
sounds?: any[];
|
|
59
|
+
semantic_ids?: string[];
|
|
60
|
+
clone_ids?: string[];
|
|
57
61
|
}
|
|
58
62
|
export interface AACTree {
|
|
59
63
|
pages: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willwade/aac-processors",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|