Skip to content

Commit 8ec7f9c

Browse files
committed
add better smart grammar support
1 parent 5aeb522 commit 8ec7f9c

2 files changed

Lines changed: 63 additions & 16 deletions

File tree

src/utilities/analytics/metrics/core.ts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,27 @@ export class MetricsCalculator {
118118
// Update buttons using dynamic spelling effort if applicable
119119
const buttons = Array.from(knownButtons.values()).sort((a, b) => a.effort - b.effort);
120120

121-
// Calculate metrics for word forms (smart grammar predictions)
122-
const wordFormMetrics = this.calculateWordFormMetrics(tree, buttons, options);
123-
buttons.push(...wordFormMetrics);
121+
// Calculate metrics for word forms (smart grammar predictions) if enabled
122+
// Default to true if not specified
123+
const useSmartGrammar = options.useSmartGrammar !== false;
124+
if (useSmartGrammar) {
125+
const { wordFormMetrics, replacedLabels } = this.calculateWordFormMetrics(
126+
tree,
127+
buttons,
128+
options
129+
);
130+
131+
// Remove buttons that were replaced by lower-effort word forms
132+
const filteredButtons = buttons.filter((btn) => !replacedLabels.has(btn.label.toLowerCase()));
124133

125-
// Re-sort after adding word forms
126-
buttons.sort((a, b) => a.effort - b.effort);
134+
// Add word forms and re-sort
135+
filteredButtons.push(...wordFormMetrics);
136+
filteredButtons.sort((a, b) => a.effort - b.effort);
137+
138+
// Replace the original buttons array
139+
buttons.length = 0;
140+
buttons.push(...filteredButtons);
141+
}
127142

128143
// Calculate grid dimensions
129144
const grid = this.calculateGridDimensions(tree);
@@ -734,19 +749,23 @@ export class MetricsCalculator {
734749
* - Parent button's cumulative effort (to reach the button)
735750
* - + Effort to select the word form from its position in predictions grid
736751
*
752+
* If a word exists as both a regular button and a word form, the version
753+
* with lower effort is kept.
754+
*
737755
* @param tree - The AAC tree
738756
* @param buttons - Already calculated button metrics
739757
* @param options - Metrics options
740-
* @returns Array of word form button metrics
758+
* @returns Object containing word form metrics and labels that were replaced
741759
*/
742760
private calculateWordFormMetrics(
743761
tree: AACTree,
744762
buttons: ButtonMetrics[],
745763
_options: MetricsOptions = {}
746-
): ButtonMetrics[] {
764+
): { wordFormMetrics: ButtonMetrics[]; replacedLabels: Set<string> } {
747765
const wordFormMetrics: ButtonMetrics[] = [];
766+
const replacedLabels = new Set<string>();
748767

749-
// Track which buttons already exist to avoid duplicates
768+
// Track buttons by label to compare efforts
750769
const existingLabels = new Map<string, ButtonMetrics>();
751770
buttons.forEach((btn) => existingLabels.set(btn.label.toLowerCase(), btn));
752771

@@ -764,11 +783,6 @@ export class MetricsCalculator {
764783
btn.predictions.forEach((wordForm: string, index: number) => {
765784
const wordFormLower = wordForm.toLowerCase();
766785

767-
// Skip if this word form already exists as a regular button
768-
if (existingLabels.has(wordFormLower)) {
769-
return;
770-
}
771-
772786
// Calculate effort based on position in predictions array
773787
// Assume predictions are displayed in a grid layout (e.g., 2 columns)
774788
const predictionsGridCols = 2; // Typical predictions layout
@@ -784,7 +798,20 @@ export class MetricsCalculator {
784798
// Word form effort = parent button's cumulative effort + selection effort
785799
const wordFormEffort = parentMetrics.effort + predictionSelectionEffort;
786800

787-
// Mark as word form with a special ID pattern
801+
// Check if this word already exists as a regular button
802+
const existingBtn = existingLabels.get(wordFormLower);
803+
804+
// If word exists and has lower or equal effort, skip the word form
805+
if (existingBtn && existingBtn.effort <= wordFormEffort) {
806+
return;
807+
}
808+
809+
// If word exists but word form has lower effort, mark for replacement
810+
if (existingBtn && existingBtn.effort > wordFormEffort) {
811+
replacedLabels.add(wordFormLower);
812+
}
813+
814+
// Create word form metric
788815
const wordFormBtn: ButtonMetrics = {
789816
id: `${btn.id}_wordform_${index}`,
790817
label: wordForm,
@@ -806,9 +833,14 @@ export class MetricsCalculator {
806833
});
807834
});
808835

809-
console.log(`📝 Calculated ${wordFormMetrics.length} word form metrics from predictions`);
836+
console.log(
837+
`📝 Calculated ${wordFormMetrics.length} word form metrics` +
838+
(replacedLabels.size > 0
839+
? ` (${replacedLabels.size} replaced higher-effort buttons: ${Array.from(replacedLabels).join(', ')})`
840+
: '')
841+
);
810842

811-
return wordFormMetrics;
843+
return { wordFormMetrics, replacedLabels };
812844
}
813845

814846
/**

src/utilities/analytics/metrics/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@ export interface MetricsOptions {
124124
* Default is 1.5 (checking 1-2 predictions on average).
125125
*/
126126
predictionSelections?: number;
127+
128+
/**
129+
* Whether to include smart grammar word forms in metrics
130+
*
131+
* When true (default): Word forms from smart grammar predictions are included
132+
* in the metrics. If a word exists as both a regular button and a word form,
133+
* the version with lower effort is used.
134+
*
135+
* When false: Smart grammar word forms are excluded from metrics. Only actual
136+
* buttons in the tree are analyzed.
137+
*
138+
* Only applicable to systems that support smart grammar (e.g., Grid 3).
139+
* Default is true.
140+
*/
141+
useSmartGrammar?: boolean;
127142
}
128143

129144
/**

0 commit comments

Comments
 (0)