Skip to content

Commit 2a1d447

Browse files
committed
Propagate POS and add synthetic metrics
Builds maps of POS tags and prediction lists from all tree pages, then propagate POS tags to metrics buttons that lack them. For POS-tagged tree buttons with predictions that aren't present in the BFS-reachable metrics set, create synthetic ButtonMetrics (high effort/depth) so their word forms can still be generated. Also fall back to a label-based lookup when resolving a button's parent metrics. This ensures POS and prediction data from topic/unreachable pages are included in metric calculations.
1 parent 374e2fd commit 2a1d447

1 file changed

Lines changed: 67 additions & 2 deletions

File tree

  • src/utilities/analytics/metrics

src/utilities/analytics/metrics/core.ts

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,14 +828,79 @@ export class MetricsCalculator {
828828
const existingLabels = new Map<string, ButtonMetrics>();
829829
buttons.forEach((btn) => existingLabels.set(btn.label.toLowerCase(), btn));
830830

831+
// Build a map of POS tags from ALL tree buttons, keyed by lowercase label.
832+
// This ensures words on BFS-unreachable pages still contribute POS data.
833+
const treePosMap = new Map<string, string>();
834+
const treePredictionsMap = new Map<string, string[]>();
835+
Object.values(tree.pages).forEach((page: AACPage) => {
836+
page.grid.forEach((row: (AACButton | null)[]) => {
837+
row.forEach((btn: AACButton | null) => {
838+
if (!btn || !btn.label) return;
839+
const lower = btn.label.toLowerCase();
840+
if (btn.pos && btn.pos !== 'Unknown' && btn.pos !== 'Ignore') {
841+
treePosMap.set(lower, btn.pos);
842+
}
843+
if (btn.predictions && btn.predictions.length > 0) {
844+
const existing = treePredictionsMap.get(lower);
845+
if (!existing || btn.predictions.length > existing.length) {
846+
treePredictionsMap.set(lower, btn.predictions);
847+
}
848+
}
849+
});
850+
});
851+
});
852+
853+
// For metrics buttons that lack POS but have a tree counterpart with POS,
854+
// propagate the POS tag so it's available in the output.
855+
buttons.forEach((btn) => {
856+
const lower = btn.label.toLowerCase();
857+
if (!btn.pos || btn.pos === 'Unknown' || btn.pos === 'Ignore') {
858+
const treePos = treePosMap.get(lower);
859+
if (treePos) btn.pos = treePos;
860+
}
861+
});
862+
863+
// For POS-tagged tree buttons that have predictions but are NOT in the
864+
// BFS-reachable metrics set, create synthetic metrics entries so their
865+
// word forms can still be generated. This handles words like "run" that
866+
// exist only on topic pages the BFS doesn't reach.
867+
const processedPredictionLabels = new Set<string>();
868+
Object.values(tree.pages).forEach((page: AACPage) => {
869+
page.grid.forEach((row: (AACButton | null)[]) => {
870+
row.forEach((btn: AACButton | null) => {
871+
if (!btn || !btn.label || !btn.predictions || btn.predictions.length === 0) return;
872+
const lower = btn.label.toLowerCase();
873+
if (existingLabels.has(lower)) return; // Already in metrics
874+
if (processedPredictionLabels.has(lower)) return; // Already synthesized
875+
processedPredictionLabels.add(lower);
876+
877+
// Create a synthetic metrics entry using a high effort estimate
878+
// (the user would need to navigate to this page to access the button)
879+
const syntheticMetrics: ButtonMetrics = {
880+
id: btn.id || `synthetic_${lower}`,
881+
label: btn.label,
882+
level: 3, // Approximate depth for unreachable pages
883+
effort: 20, // High effort — deep navigation required
884+
count: 1,
885+
pos: btn.pos,
886+
};
887+
buttons.push(syntheticMetrics);
888+
existingLabels.set(lower, syntheticMetrics);
889+
});
890+
});
891+
});
892+
831893
// Iterate through all pages to find buttons with predictions
832894
Object.values(tree.pages).forEach((page: AACPage) => {
833895
page.grid.forEach((row: (AACButton | null)[]) => {
834896
row.forEach((btn: AACButton | null) => {
835897
if (!btn || !btn.predictions || btn.predictions.length === 0) return;
836898

837-
// Find the parent button's metrics
838-
const parentMetrics = buttons.find((b) => b.id === btn.id);
899+
// Find the parent button's metrics (by id first, then by label)
900+
let parentMetrics = buttons.find((b) => b.id === btn.id);
901+
if (!parentMetrics && btn.label) {
902+
parentMetrics = existingLabels.get(btn.label.toLowerCase());
903+
}
839904
if (!parentMetrics) return;
840905

841906
// Calculate effort for each word form

0 commit comments

Comments
 (0)