@willwade/aac-processors 0.1.7 → 0.1.8
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/analytics.d.ts +7 -0
- package/dist/analytics.js +23 -0
- package/dist/browser/index.browser.js +5 -0
- package/dist/browser/metrics.js +17 -0
- package/dist/browser/processors/gridset/helpers.js +390 -0
- package/dist/browser/processors/snap/helpers.js +252 -0
- package/dist/browser/utilities/analytics/history.js +116 -0
- package/dist/browser/utilities/analytics/metrics/comparison.js +477 -0
- package/dist/browser/utilities/analytics/metrics/core.js +775 -0
- package/dist/browser/utilities/analytics/metrics/effort.js +221 -0
- package/dist/browser/utilities/analytics/metrics/obl-types.js +6 -0
- package/dist/browser/utilities/analytics/metrics/obl.js +282 -0
- package/dist/browser/utilities/analytics/metrics/sentence.js +121 -0
- package/dist/browser/utilities/analytics/metrics/types.js +6 -0
- package/dist/browser/utilities/analytics/metrics/vocabulary.js +138 -0
- package/dist/browser/utilities/analytics/reference/browser.js +67 -0
- package/dist/browser/utilities/analytics/reference/index.js +129 -0
- package/dist/browser/utils/dotnetTicks.js +17 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +18 -1
- package/dist/index.node.d.ts +2 -2
- package/dist/index.node.js +5 -5
- package/dist/metrics.d.ts +17 -0
- package/dist/metrics.js +44 -0
- package/dist/utilities/analytics/metrics/comparison.d.ts +2 -1
- package/dist/utilities/analytics/metrics/comparison.js +3 -3
- package/dist/utilities/analytics/metrics/vocabulary.d.ts +2 -2
- package/dist/utilities/analytics/reference/browser.d.ts +31 -0
- package/dist/utilities/analytics/reference/browser.js +73 -0
- package/dist/utilities/analytics/reference/index.d.ts +21 -0
- package/dist/utilities/analytics/reference/index.js +22 -46
- package/package.json +9 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabulary Coverage Analysis
|
|
3
|
+
*
|
|
4
|
+
* Analyzes how well an AAC board set covers core vocabulary
|
|
5
|
+
* and identifies missing/extra words compared to reference lists.
|
|
6
|
+
*/
|
|
7
|
+
import { ReferenceLoader } from '../reference/index';
|
|
8
|
+
import { spellingEffort } from './effort';
|
|
9
|
+
export class VocabularyAnalyzer {
|
|
10
|
+
constructor(referenceLoader) {
|
|
11
|
+
this.referenceLoader = referenceLoader || new ReferenceLoader();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Analyze vocabulary coverage against core lists
|
|
15
|
+
*/
|
|
16
|
+
analyze(metrics, options) {
|
|
17
|
+
// const locale = options?.locale || metrics.locale || 'en';
|
|
18
|
+
const highEffortThreshold = options?.highEffortThreshold || 5.0;
|
|
19
|
+
const lowEffortThreshold = options?.lowEffortThreshold || 2.0;
|
|
20
|
+
// Load reference data
|
|
21
|
+
const coreLists = this.referenceLoader.loadCoreLists();
|
|
22
|
+
// Create word to effort map (using lowercase keys for matching)
|
|
23
|
+
const wordEffortMap = new Map();
|
|
24
|
+
metrics.buttons.forEach((btn) => {
|
|
25
|
+
const word = btn.label.toLowerCase();
|
|
26
|
+
const existing = wordEffortMap.get(word);
|
|
27
|
+
if (!existing || btn.effort < existing) {
|
|
28
|
+
wordEffortMap.set(word, btn.effort);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
// Analyze each core list
|
|
32
|
+
const core_coverage = {};
|
|
33
|
+
coreLists.forEach((list) => {
|
|
34
|
+
const analysis = this.analyzeCoreList(list, wordEffortMap);
|
|
35
|
+
core_coverage[list.id] = analysis;
|
|
36
|
+
});
|
|
37
|
+
// Find extra words (words not in any core list)
|
|
38
|
+
const allCoreWords = new Set();
|
|
39
|
+
coreLists.forEach((list) => {
|
|
40
|
+
list.words.forEach((word) => allCoreWords.add(word.toLowerCase()));
|
|
41
|
+
});
|
|
42
|
+
const extraWords = [];
|
|
43
|
+
wordEffortMap.forEach((effort, word) => {
|
|
44
|
+
if (!allCoreWords.has(word.toLowerCase())) {
|
|
45
|
+
extraWords.push(word);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
extraWords.sort((a, b) => a.localeCompare(b));
|
|
49
|
+
// Find high/low effort words
|
|
50
|
+
const highEffortWords = [];
|
|
51
|
+
const lowEffortWords = [];
|
|
52
|
+
wordEffortMap.forEach((effort, word) => {
|
|
53
|
+
if (effort > highEffortThreshold) {
|
|
54
|
+
highEffortWords.push({ word, effort });
|
|
55
|
+
}
|
|
56
|
+
else if (effort < lowEffortThreshold) {
|
|
57
|
+
lowEffortWords.push({ word, effort });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
highEffortWords.sort((a, b) => b.effort - a.effort);
|
|
61
|
+
lowEffortWords.sort((a, b) => a.effort - b.effort);
|
|
62
|
+
return {
|
|
63
|
+
core_coverage,
|
|
64
|
+
total_unique_words: wordEffortMap.size,
|
|
65
|
+
words_with_effort: wordEffortMap.size,
|
|
66
|
+
words_requiring_spelling: 0, // Calculated during sentence analysis
|
|
67
|
+
extra_words: extraWords,
|
|
68
|
+
high_effort_words: highEffortWords.slice(0, 50), // Top 50
|
|
69
|
+
low_effort_words: lowEffortWords.slice(0, 50), // Bottom 50
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Analyze coverage for a single core list
|
|
74
|
+
*/
|
|
75
|
+
analyzeCoreList(list, wordEffortMap) {
|
|
76
|
+
const covered = [];
|
|
77
|
+
const missing = [];
|
|
78
|
+
let totalEffort = 0;
|
|
79
|
+
list.words.forEach((word) => {
|
|
80
|
+
const lowerWord = word.toLowerCase();
|
|
81
|
+
const effort = wordEffortMap.get(lowerWord);
|
|
82
|
+
if (effort !== undefined) {
|
|
83
|
+
covered.push(word);
|
|
84
|
+
totalEffort += effort;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
missing.push(word);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const averageEffort = covered.length > 0 ? totalEffort / covered.length : 0;
|
|
91
|
+
return {
|
|
92
|
+
name: list.name,
|
|
93
|
+
total_words: list.words.length,
|
|
94
|
+
covered: covered.length,
|
|
95
|
+
missing: missing.length,
|
|
96
|
+
coverage_percent: (covered.length / list.words.length) * 100,
|
|
97
|
+
missing_words: missing,
|
|
98
|
+
average_effort: averageEffort,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Calculate coverage percentage for a specific word list
|
|
103
|
+
*/
|
|
104
|
+
calculateCoverage(wordList, metrics) {
|
|
105
|
+
const wordSet = new Set(metrics.buttons.map((btn) => btn.label.toLowerCase()));
|
|
106
|
+
const covered = [];
|
|
107
|
+
const missing = [];
|
|
108
|
+
wordList.forEach((word) => {
|
|
109
|
+
if (wordSet.has(word.toLowerCase())) {
|
|
110
|
+
covered.push(word);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
missing.push(word);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
covered,
|
|
118
|
+
missing,
|
|
119
|
+
coverage_percent: (covered.length / wordList.length) * 100,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get effort for a word, or calculate spelling effort if missing
|
|
124
|
+
*/
|
|
125
|
+
getWordEffort(word, metrics) {
|
|
126
|
+
const btn = metrics.buttons.find((b) => b.label.toLowerCase() === word.toLowerCase());
|
|
127
|
+
if (btn) {
|
|
128
|
+
return btn.effort;
|
|
129
|
+
}
|
|
130
|
+
return spellingEffort(word, metrics.spelling_effort_base, metrics.spelling_effort_per_letter);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if a word is in the board set
|
|
134
|
+
*/
|
|
135
|
+
hasWord(word, metrics) {
|
|
136
|
+
return metrics.buttons.some((b) => b.label.toLowerCase() === word.toLowerCase());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-friendly reference data loader using fetch.
|
|
3
|
+
*/
|
|
4
|
+
export class InMemoryReferenceLoader {
|
|
5
|
+
constructor(data) {
|
|
6
|
+
this.data = data;
|
|
7
|
+
}
|
|
8
|
+
loadCoreLists() {
|
|
9
|
+
return this.data.coreLists;
|
|
10
|
+
}
|
|
11
|
+
loadCommonWords() {
|
|
12
|
+
return this.data.commonWords;
|
|
13
|
+
}
|
|
14
|
+
loadSynonyms() {
|
|
15
|
+
return this.data.synonyms;
|
|
16
|
+
}
|
|
17
|
+
loadSentences() {
|
|
18
|
+
return this.data.sentences;
|
|
19
|
+
}
|
|
20
|
+
loadFringe() {
|
|
21
|
+
return this.data.fringe;
|
|
22
|
+
}
|
|
23
|
+
loadBaseWords() {
|
|
24
|
+
return this.data.baseWords;
|
|
25
|
+
}
|
|
26
|
+
loadCommonFringe() {
|
|
27
|
+
const commonWords = new Set(this.data.commonWords.words.map((w) => w.toLowerCase()));
|
|
28
|
+
const coreWords = new Set();
|
|
29
|
+
this.data.coreLists.forEach((list) => {
|
|
30
|
+
list.words.forEach((word) => coreWords.add(word.toLowerCase()));
|
|
31
|
+
});
|
|
32
|
+
return Array.from(commonWords).filter((word) => !coreWords.has(word));
|
|
33
|
+
}
|
|
34
|
+
loadAll() {
|
|
35
|
+
return this.data;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function loadReferenceDataFromUrl(baseUrl, locale = 'en') {
|
|
39
|
+
const root = baseUrl.replace(/\/$/, '');
|
|
40
|
+
const fetchJson = async (name) => {
|
|
41
|
+
const res = await fetch(`${root}/${name}.${locale}.json`);
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
throw new Error(`Failed to load ${name}.${locale}.json`);
|
|
44
|
+
}
|
|
45
|
+
return (await res.json());
|
|
46
|
+
};
|
|
47
|
+
const [coreLists, commonWords, synonyms, sentences, fringe, baseWords] = await Promise.all([
|
|
48
|
+
fetchJson('core_lists'),
|
|
49
|
+
fetchJson('common_words'),
|
|
50
|
+
fetchJson('synonyms'),
|
|
51
|
+
fetchJson('sentences'),
|
|
52
|
+
fetchJson('fringe'),
|
|
53
|
+
fetchJson('base_words'),
|
|
54
|
+
]);
|
|
55
|
+
return {
|
|
56
|
+
coreLists,
|
|
57
|
+
commonWords,
|
|
58
|
+
synonyms,
|
|
59
|
+
sentences,
|
|
60
|
+
fringe,
|
|
61
|
+
baseWords,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export async function createBrowserReferenceLoader(baseUrl, locale = 'en') {
|
|
65
|
+
const data = await loadReferenceDataFromUrl(baseUrl, locale);
|
|
66
|
+
return new InMemoryReferenceLoader(data);
|
|
67
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference Data Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads reference vocabulary lists, core lists, and sentences
|
|
5
|
+
* for AAC metrics analysis.
|
|
6
|
+
*/
|
|
7
|
+
import { getFs, getPath } from '../../../utils/io';
|
|
8
|
+
export class ReferenceLoader {
|
|
9
|
+
constructor(dataDir, locale = 'en') {
|
|
10
|
+
this.locale = locale;
|
|
11
|
+
if (dataDir) {
|
|
12
|
+
this.dataDir = dataDir;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
// Resolve the data directory relative to this file's location
|
|
16
|
+
// Use __dirname which works correctly after compilation
|
|
17
|
+
this.dataDir = getPath().join(__dirname, 'data');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Load core vocabulary lists
|
|
22
|
+
*/
|
|
23
|
+
loadCoreLists() {
|
|
24
|
+
const filePath = getPath().join(this.dataDir, `core_lists.${this.locale}.json`);
|
|
25
|
+
const content = getFs().readFileSync(filePath, 'utf-8');
|
|
26
|
+
return JSON.parse(String(content));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load common words with baseline effort scores
|
|
30
|
+
*/
|
|
31
|
+
loadCommonWords() {
|
|
32
|
+
const filePath = getPath().join(this.dataDir, `common_words.${this.locale}.json`);
|
|
33
|
+
const content = getFs().readFileSync(filePath, 'utf-8');
|
|
34
|
+
return JSON.parse(String(content));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load synonym mappings
|
|
38
|
+
*/
|
|
39
|
+
loadSynonyms() {
|
|
40
|
+
const filePath = getPath().join(this.dataDir, `synonyms.${this.locale}.json`);
|
|
41
|
+
const content = getFs().readFileSync(filePath, 'utf-8');
|
|
42
|
+
return JSON.parse(String(content));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Load test sentences
|
|
46
|
+
*/
|
|
47
|
+
loadSentences() {
|
|
48
|
+
const filePath = getPath().join(this.dataDir, `sentences.${this.locale}.json`);
|
|
49
|
+
const content = getFs().readFileSync(filePath, 'utf-8');
|
|
50
|
+
return JSON.parse(String(content));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load fringe vocabulary
|
|
54
|
+
*/
|
|
55
|
+
loadFringe() {
|
|
56
|
+
const filePath = getPath().join(this.dataDir, `fringe.${this.locale}.json`);
|
|
57
|
+
const content = getFs().readFileSync(filePath, 'utf-8');
|
|
58
|
+
const data = JSON.parse(String(content));
|
|
59
|
+
// Flatten nested category words if needed
|
|
60
|
+
if (Array.isArray(data) && data.length > 0 && data[0].categories) {
|
|
61
|
+
const flattened = [];
|
|
62
|
+
data.forEach((list) => {
|
|
63
|
+
list.categories.forEach((cat) => {
|
|
64
|
+
flattened.push(...cat.words);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
return flattened;
|
|
68
|
+
}
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Load base words hash map
|
|
73
|
+
*/
|
|
74
|
+
loadBaseWords() {
|
|
75
|
+
const filePath = getPath().join(this.dataDir, `base_words.${this.locale}.json`);
|
|
76
|
+
const content = getFs().readFileSync(filePath, 'utf-8');
|
|
77
|
+
return JSON.parse(String(content));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Load common fringe vocabulary
|
|
81
|
+
* Common words that are NOT in core vocabulary lists
|
|
82
|
+
* (matching Ruby loader.rb:413-420)
|
|
83
|
+
*/
|
|
84
|
+
loadCommonFringe() {
|
|
85
|
+
const commonWordsData = this.loadCommonWords();
|
|
86
|
+
const commonWords = new Set(commonWordsData.words.map((w) => w.toLowerCase()));
|
|
87
|
+
const coreLists = this.loadCoreLists();
|
|
88
|
+
const coreWords = new Set();
|
|
89
|
+
coreLists.forEach((list) => {
|
|
90
|
+
list.words.forEach((word) => coreWords.add(word.toLowerCase()));
|
|
91
|
+
});
|
|
92
|
+
// Common fringe = common words - core words
|
|
93
|
+
const commonFringe = Array.from(commonWords).filter((word) => !coreWords.has(word));
|
|
94
|
+
return commonFringe;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get all reference data at once
|
|
98
|
+
*/
|
|
99
|
+
loadAll() {
|
|
100
|
+
return {
|
|
101
|
+
coreLists: this.loadCoreLists(),
|
|
102
|
+
commonWords: this.loadCommonWords(),
|
|
103
|
+
synonyms: this.loadSynonyms(),
|
|
104
|
+
sentences: this.loadSentences(),
|
|
105
|
+
fringe: this.loadFringe(),
|
|
106
|
+
baseWords: this.loadBaseWords(),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the default reference data path
|
|
112
|
+
*/
|
|
113
|
+
export function getReferenceDataPath() {
|
|
114
|
+
return String(getPath().join(__dirname, 'data'));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if reference data files exist
|
|
118
|
+
*/
|
|
119
|
+
export function hasReferenceData() {
|
|
120
|
+
const dataPath = getReferenceDataPath();
|
|
121
|
+
const requiredFiles = [
|
|
122
|
+
'core_lists.en.json',
|
|
123
|
+
'common_words.en.json',
|
|
124
|
+
'sentences.en.json',
|
|
125
|
+
'synonyms.en.json',
|
|
126
|
+
'fringe.en.json',
|
|
127
|
+
];
|
|
128
|
+
return requiredFiles.every((file) => getFs().existsSync(getPath().join(dataPath, file)));
|
|
129
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number of ticks (.NET 100ns units) between 0001-01-01 and Unix epoch.
|
|
3
|
+
*/
|
|
4
|
+
export const DOTNET_EPOCH_TICKS = 621355968000000000n;
|
|
5
|
+
/**
|
|
6
|
+
* Number of ticks per millisecond.
|
|
7
|
+
*/
|
|
8
|
+
export const TICKS_PER_MILLISECOND = 10000n;
|
|
9
|
+
/**
|
|
10
|
+
* Convert .NET ticks (100ns since 0001-01-01) to a JavaScript Date.
|
|
11
|
+
* Accepts bigint or number and rounds down to millisecond precision.
|
|
12
|
+
*/
|
|
13
|
+
export function dotNetTicksToDate(ticks) {
|
|
14
|
+
const tickValue = BigInt(ticks);
|
|
15
|
+
const ms = Number((tickValue - DOTNET_EPOCH_TICKS) / TICKS_PER_MILLISECOND);
|
|
16
|
+
return new Date(ms);
|
|
17
|
+
}
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { SnapProcessor } from './processors/snapProcessor';
|
|
|
22
22
|
export { TouchChatProcessor } from './processors/touchchatProcessor';
|
|
23
23
|
export { ApplePanelsProcessor } from './processors/applePanelsProcessor';
|
|
24
24
|
export { AstericsGridProcessor } from './processors/astericsGridProcessor';
|
|
25
|
+
export * as Metrics from './metrics';
|
|
25
26
|
import { BaseProcessor } from './core/baseProcessor';
|
|
26
27
|
export { configureSqlJs } from './utils/sqlite';
|
|
27
28
|
/**
|
package/dist/index.browser.js
CHANGED
|
@@ -23,11 +23,23 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
23
23
|
if (k2 === undefined) k2 = k;
|
|
24
24
|
o[k2] = m[k];
|
|
25
25
|
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
26
31
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
27
32
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
28
33
|
};
|
|
34
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
29
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.configureSqlJs = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
|
|
42
|
+
exports.configureSqlJs = exports.Metrics = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
|
|
31
43
|
exports.getProcessor = getProcessor;
|
|
32
44
|
exports.getSupportedExtensions = getSupportedExtensions;
|
|
33
45
|
exports.isExtensionSupported = isExtensionSupported;
|
|
@@ -56,6 +68,11 @@ var applePanelsProcessor_1 = require("./processors/applePanelsProcessor");
|
|
|
56
68
|
Object.defineProperty(exports, "ApplePanelsProcessor", { enumerable: true, get: function () { return applePanelsProcessor_1.ApplePanelsProcessor; } });
|
|
57
69
|
var astericsGridProcessor_1 = require("./processors/astericsGridProcessor");
|
|
58
70
|
Object.defineProperty(exports, "AstericsGridProcessor", { enumerable: true, get: function () { return astericsGridProcessor_1.AstericsGridProcessor; } });
|
|
71
|
+
// ===================================================================
|
|
72
|
+
// UTILITY FUNCTIONS
|
|
73
|
+
// ===================================================================
|
|
74
|
+
// Metrics namespace (pageset analytics)
|
|
75
|
+
exports.Metrics = __importStar(require("./metrics"));
|
|
59
76
|
const dotProcessor_2 = require("./processors/dotProcessor");
|
|
60
77
|
const opmlProcessor_2 = require("./processors/opmlProcessor");
|
|
61
78
|
const obfProcessor_2 = require("./processors/obfProcessor");
|
package/dist/index.node.d.ts
CHANGED
|
@@ -9,9 +9,9 @@ export * from './core/treeStructure';
|
|
|
9
9
|
export * from './core/baseProcessor';
|
|
10
10
|
export * from './core/stringCasing';
|
|
11
11
|
export * from './processors';
|
|
12
|
-
export * as Analytics from './
|
|
13
|
-
export * from './utilities/analytics';
|
|
12
|
+
export * as Analytics from './analytics';
|
|
14
13
|
export * as Validation from './validation';
|
|
14
|
+
export * as Metrics from './metrics';
|
|
15
15
|
export * as Gridset from './gridset';
|
|
16
16
|
export * as Snap from './snap';
|
|
17
17
|
export * as OBF from './obf';
|
package/dist/index.node.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.Validation = exports.Analytics = void 0;
|
|
36
|
+
exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.Metrics = exports.Validation = exports.Analytics = void 0;
|
|
37
37
|
exports.getProcessor = getProcessor;
|
|
38
38
|
exports.getSupportedExtensions = getSupportedExtensions;
|
|
39
39
|
exports.isExtensionSupported = isExtensionSupported;
|
|
@@ -50,12 +50,12 @@ __exportStar(require("./processors"), exports);
|
|
|
50
50
|
// ===================================================================
|
|
51
51
|
// NAMESPACES
|
|
52
52
|
// ===================================================================
|
|
53
|
-
// Analytics namespace
|
|
54
|
-
exports.Analytics = __importStar(require("./
|
|
55
|
-
// Also export analytics classes directly for convenience
|
|
56
|
-
__exportStar(require("./utilities/analytics"), exports);
|
|
53
|
+
// Analytics namespace (usage/history)
|
|
54
|
+
exports.Analytics = __importStar(require("./analytics"));
|
|
57
55
|
// Validation namespace
|
|
58
56
|
exports.Validation = __importStar(require("./validation"));
|
|
57
|
+
// Metrics namespace (pageset analytics)
|
|
58
|
+
exports.Metrics = __importStar(require("./metrics"));
|
|
59
59
|
// Processor namespaces (platform-specific utilities)
|
|
60
60
|
exports.Gridset = __importStar(require("./gridset"));
|
|
61
61
|
exports.Snap = __importStar(require("./snap"));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Namespace
|
|
3
|
+
*
|
|
4
|
+
* Pageset-focused metrics and analytics (board structure, effort, vocabulary).
|
|
5
|
+
* Use this for analyzing AAC trees, not end-user usage logs.
|
|
6
|
+
*/
|
|
7
|
+
export * from './utilities/analytics/metrics/types';
|
|
8
|
+
export * from './utilities/analytics/metrics/effort';
|
|
9
|
+
export * from './utilities/analytics/metrics/obl-types';
|
|
10
|
+
export { OblUtil, OblAnonymizer } from './utilities/analytics/metrics/obl';
|
|
11
|
+
export { MetricsCalculator } from './utilities/analytics/metrics/core';
|
|
12
|
+
export { VocabularyAnalyzer } from './utilities/analytics/metrics/vocabulary';
|
|
13
|
+
export { SentenceAnalyzer } from './utilities/analytics/metrics/sentence';
|
|
14
|
+
export { ComparisonAnalyzer } from './utilities/analytics/metrics/comparison';
|
|
15
|
+
export { ReferenceLoader } from './utilities/analytics/reference';
|
|
16
|
+
export { InMemoryReferenceLoader, createBrowserReferenceLoader, loadReferenceDataFromUrl, type ReferenceData, } from './utilities/analytics/reference/browser';
|
|
17
|
+
export * from './utilities/analytics/utils/idGenerator';
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Metrics Namespace
|
|
4
|
+
*
|
|
5
|
+
* Pageset-focused metrics and analytics (board structure, effort, vocabulary).
|
|
6
|
+
* Use this for analyzing AAC trees, not end-user usage logs.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
20
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.loadReferenceDataFromUrl = exports.createBrowserReferenceLoader = exports.InMemoryReferenceLoader = exports.ReferenceLoader = exports.ComparisonAnalyzer = exports.SentenceAnalyzer = exports.VocabularyAnalyzer = exports.MetricsCalculator = exports.OblAnonymizer = exports.OblUtil = void 0;
|
|
24
|
+
__exportStar(require("./utilities/analytics/metrics/types"), exports);
|
|
25
|
+
__exportStar(require("./utilities/analytics/metrics/effort"), exports);
|
|
26
|
+
__exportStar(require("./utilities/analytics/metrics/obl-types"), exports);
|
|
27
|
+
var obl_1 = require("./utilities/analytics/metrics/obl");
|
|
28
|
+
Object.defineProperty(exports, "OblUtil", { enumerable: true, get: function () { return obl_1.OblUtil; } });
|
|
29
|
+
Object.defineProperty(exports, "OblAnonymizer", { enumerable: true, get: function () { return obl_1.OblAnonymizer; } });
|
|
30
|
+
var core_1 = require("./utilities/analytics/metrics/core");
|
|
31
|
+
Object.defineProperty(exports, "MetricsCalculator", { enumerable: true, get: function () { return core_1.MetricsCalculator; } });
|
|
32
|
+
var vocabulary_1 = require("./utilities/analytics/metrics/vocabulary");
|
|
33
|
+
Object.defineProperty(exports, "VocabularyAnalyzer", { enumerable: true, get: function () { return vocabulary_1.VocabularyAnalyzer; } });
|
|
34
|
+
var sentence_1 = require("./utilities/analytics/metrics/sentence");
|
|
35
|
+
Object.defineProperty(exports, "SentenceAnalyzer", { enumerable: true, get: function () { return sentence_1.SentenceAnalyzer; } });
|
|
36
|
+
var comparison_1 = require("./utilities/analytics/metrics/comparison");
|
|
37
|
+
Object.defineProperty(exports, "ComparisonAnalyzer", { enumerable: true, get: function () { return comparison_1.ComparisonAnalyzer; } });
|
|
38
|
+
var reference_1 = require("./utilities/analytics/reference");
|
|
39
|
+
Object.defineProperty(exports, "ReferenceLoader", { enumerable: true, get: function () { return reference_1.ReferenceLoader; } });
|
|
40
|
+
var browser_1 = require("./utilities/analytics/reference/browser");
|
|
41
|
+
Object.defineProperty(exports, "InMemoryReferenceLoader", { enumerable: true, get: function () { return browser_1.InMemoryReferenceLoader; } });
|
|
42
|
+
Object.defineProperty(exports, "createBrowserReferenceLoader", { enumerable: true, get: function () { return browser_1.createBrowserReferenceLoader; } });
|
|
43
|
+
Object.defineProperty(exports, "loadReferenceDataFromUrl", { enumerable: true, get: function () { return browser_1.loadReferenceDataFromUrl; } });
|
|
44
|
+
__exportStar(require("./utilities/analytics/utils/idGenerator"), exports);
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
* analyze vocabulary differences, and generate CARE component scores.
|
|
6
6
|
*/
|
|
7
7
|
import { MetricsResult, ComparisonResult } from './types';
|
|
8
|
+
import { type ReferenceDataProvider } from '../reference/index';
|
|
8
9
|
import { MetricsOptions } from './types';
|
|
9
10
|
export declare class ComparisonAnalyzer {
|
|
10
11
|
private vocabAnalyzer;
|
|
11
12
|
private sentenceAnalyzer;
|
|
12
13
|
private referenceLoader;
|
|
13
|
-
constructor();
|
|
14
|
+
constructor(referenceLoader?: ReferenceDataProvider);
|
|
14
15
|
private normalize;
|
|
15
16
|
/**
|
|
16
17
|
* Compare two board sets
|
|
@@ -12,10 +12,10 @@ const vocabulary_1 = require("./vocabulary");
|
|
|
12
12
|
const index_1 = require("../reference/index");
|
|
13
13
|
const effort_1 = require("./effort");
|
|
14
14
|
class ComparisonAnalyzer {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.vocabAnalyzer = new vocabulary_1.VocabularyAnalyzer();
|
|
15
|
+
constructor(referenceLoader) {
|
|
16
|
+
this.vocabAnalyzer = new vocabulary_1.VocabularyAnalyzer(referenceLoader);
|
|
17
17
|
this.sentenceAnalyzer = new sentence_1.SentenceAnalyzer();
|
|
18
|
-
this.referenceLoader = new index_1.ReferenceLoader();
|
|
18
|
+
this.referenceLoader = referenceLoader || new index_1.ReferenceLoader();
|
|
19
19
|
}
|
|
20
20
|
normalize(word) {
|
|
21
21
|
return word
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and identifies missing/extra words compared to reference lists.
|
|
6
6
|
*/
|
|
7
7
|
import { MetricsResult } from './types';
|
|
8
|
-
import {
|
|
8
|
+
import { type ReferenceDataProvider } from '../reference/index';
|
|
9
9
|
export interface VocabularyAnalysis {
|
|
10
10
|
core_coverage: {
|
|
11
11
|
[listId: string]: {
|
|
@@ -33,7 +33,7 @@ export interface VocabularyAnalysis {
|
|
|
33
33
|
}
|
|
34
34
|
export declare class VocabularyAnalyzer {
|
|
35
35
|
private referenceLoader;
|
|
36
|
-
constructor(referenceLoader?:
|
|
36
|
+
constructor(referenceLoader?: ReferenceDataProvider);
|
|
37
37
|
/**
|
|
38
38
|
* Analyze vocabulary coverage against core lists
|
|
39
39
|
*/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-friendly reference data loader using fetch.
|
|
3
|
+
*/
|
|
4
|
+
import type { CoreList, CommonWordsData, SynonymsData } from '../metrics/types';
|
|
5
|
+
import type { ReferenceDataProvider } from './index';
|
|
6
|
+
export interface ReferenceData {
|
|
7
|
+
coreLists: CoreList[];
|
|
8
|
+
commonWords: CommonWordsData;
|
|
9
|
+
synonyms: SynonymsData;
|
|
10
|
+
sentences: string[][];
|
|
11
|
+
fringe: string[];
|
|
12
|
+
baseWords: {
|
|
13
|
+
[word: string]: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare class InMemoryReferenceLoader implements ReferenceDataProvider {
|
|
17
|
+
private data;
|
|
18
|
+
constructor(data: ReferenceData);
|
|
19
|
+
loadCoreLists(): CoreList[];
|
|
20
|
+
loadCommonWords(): CommonWordsData;
|
|
21
|
+
loadSynonyms(): SynonymsData;
|
|
22
|
+
loadSentences(): string[][];
|
|
23
|
+
loadFringe(): string[];
|
|
24
|
+
loadBaseWords(): {
|
|
25
|
+
[word: string]: boolean;
|
|
26
|
+
};
|
|
27
|
+
loadCommonFringe(): string[];
|
|
28
|
+
loadAll(): ReferenceData;
|
|
29
|
+
}
|
|
30
|
+
export declare function loadReferenceDataFromUrl(baseUrl: string, locale?: string): Promise<ReferenceData>;
|
|
31
|
+
export declare function createBrowserReferenceLoader(baseUrl: string, locale?: string): Promise<InMemoryReferenceLoader>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Browser-friendly reference data loader using fetch.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.InMemoryReferenceLoader = void 0;
|
|
7
|
+
exports.loadReferenceDataFromUrl = loadReferenceDataFromUrl;
|
|
8
|
+
exports.createBrowserReferenceLoader = createBrowserReferenceLoader;
|
|
9
|
+
class InMemoryReferenceLoader {
|
|
10
|
+
constructor(data) {
|
|
11
|
+
this.data = data;
|
|
12
|
+
}
|
|
13
|
+
loadCoreLists() {
|
|
14
|
+
return this.data.coreLists;
|
|
15
|
+
}
|
|
16
|
+
loadCommonWords() {
|
|
17
|
+
return this.data.commonWords;
|
|
18
|
+
}
|
|
19
|
+
loadSynonyms() {
|
|
20
|
+
return this.data.synonyms;
|
|
21
|
+
}
|
|
22
|
+
loadSentences() {
|
|
23
|
+
return this.data.sentences;
|
|
24
|
+
}
|
|
25
|
+
loadFringe() {
|
|
26
|
+
return this.data.fringe;
|
|
27
|
+
}
|
|
28
|
+
loadBaseWords() {
|
|
29
|
+
return this.data.baseWords;
|
|
30
|
+
}
|
|
31
|
+
loadCommonFringe() {
|
|
32
|
+
const commonWords = new Set(this.data.commonWords.words.map((w) => w.toLowerCase()));
|
|
33
|
+
const coreWords = new Set();
|
|
34
|
+
this.data.coreLists.forEach((list) => {
|
|
35
|
+
list.words.forEach((word) => coreWords.add(word.toLowerCase()));
|
|
36
|
+
});
|
|
37
|
+
return Array.from(commonWords).filter((word) => !coreWords.has(word));
|
|
38
|
+
}
|
|
39
|
+
loadAll() {
|
|
40
|
+
return this.data;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.InMemoryReferenceLoader = InMemoryReferenceLoader;
|
|
44
|
+
async function loadReferenceDataFromUrl(baseUrl, locale = 'en') {
|
|
45
|
+
const root = baseUrl.replace(/\/$/, '');
|
|
46
|
+
const fetchJson = async (name) => {
|
|
47
|
+
const res = await fetch(`${root}/${name}.${locale}.json`);
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
throw new Error(`Failed to load ${name}.${locale}.json`);
|
|
50
|
+
}
|
|
51
|
+
return (await res.json());
|
|
52
|
+
};
|
|
53
|
+
const [coreLists, commonWords, synonyms, sentences, fringe, baseWords] = await Promise.all([
|
|
54
|
+
fetchJson('core_lists'),
|
|
55
|
+
fetchJson('common_words'),
|
|
56
|
+
fetchJson('synonyms'),
|
|
57
|
+
fetchJson('sentences'),
|
|
58
|
+
fetchJson('fringe'),
|
|
59
|
+
fetchJson('base_words'),
|
|
60
|
+
]);
|
|
61
|
+
return {
|
|
62
|
+
coreLists,
|
|
63
|
+
commonWords,
|
|
64
|
+
synonyms,
|
|
65
|
+
sentences,
|
|
66
|
+
fringe,
|
|
67
|
+
baseWords,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function createBrowserReferenceLoader(baseUrl, locale = 'en') {
|
|
71
|
+
const data = await loadReferenceDataFromUrl(baseUrl, locale);
|
|
72
|
+
return new InMemoryReferenceLoader(data);
|
|
73
|
+
}
|