Phase 2 of Spanish-to-English translation for medium-priority files: Frontend utils (2 files): - dataTransformation.ts: Translated ~72 occurrences (comments, docs, console logs) - segmentClassifier.ts: Translated ~20 occurrences (JSDoc, inline comments, UI strings) Backend dimensions (3 files): - OperationalPerformance.py: Translated ~117 lines (docstrings, comments) - SatisfactionExperience.py: Translated ~33 lines (docstrings, comments) - EconomyCost.py: Translated ~79 lines (docstrings, comments) All function names and variable names preserved for API compatibility. Frontend and backend compilation tested and verified successful. Related to TRANSLATION_STATUS.md Phase 2 objectives. https://claude.ai/code/session_01GNbnkFoESkRcnPr3bLCYDg
201 lines
5.6 KiB
TypeScript
201 lines
5.6 KiB
TypeScript
// utils/segmentClassifier.ts
|
|
// Utility to classify queues/skills into customer segments
|
|
|
|
import type { CustomerSegment, RawInteraction, StaticConfig } from '../types';
|
|
|
|
export interface SegmentMapping {
|
|
high_value_queues: string[];
|
|
medium_value_queues: string[];
|
|
low_value_queues: string[];
|
|
}
|
|
|
|
/**
|
|
* Parses queue string separated by commas
|
|
* Example: "VIP, Premium, Enterprise" → ["VIP", "Premium", "Enterprise"]
|
|
*/
|
|
export function parseQueueList(input: string): string[] {
|
|
if (!input || input.trim().length === 0) {
|
|
return [];
|
|
}
|
|
|
|
return input
|
|
.split(',')
|
|
.map(q => q.trim())
|
|
.filter(q => q.length > 0);
|
|
}
|
|
|
|
/**
|
|
* Classifies a queue according to the provided mapping
|
|
* Uses partial and case-insensitive matching
|
|
*
|
|
* Example:
|
|
* - queue: "VIP_Support" + mapping.high: ["VIP"] → "high"
|
|
* - queue: "General_Support_L1" + mapping.medium: ["General_Support"] → "medium"
|
|
* - queue: "Retention" (no match) → "medium" (default)
|
|
*/
|
|
export function classifyQueue(
|
|
queue: string,
|
|
mapping: SegmentMapping
|
|
): CustomerSegment {
|
|
const normalizedQueue = queue.toLowerCase().trim();
|
|
|
|
// Search in high value
|
|
for (const highQueue of mapping.high_value_queues) {
|
|
const normalizedHigh = highQueue.toLowerCase().trim();
|
|
if (normalizedQueue.includes(normalizedHigh) || normalizedHigh.includes(normalizedQueue)) {
|
|
return 'high';
|
|
}
|
|
}
|
|
|
|
// Search in low value
|
|
for (const lowQueue of mapping.low_value_queues) {
|
|
const normalizedLow = lowQueue.toLowerCase().trim();
|
|
if (normalizedQueue.includes(normalizedLow) || normalizedLow.includes(normalizedQueue)) {
|
|
return 'low';
|
|
}
|
|
}
|
|
|
|
// Search in medium value (explicit)
|
|
for (const mediumQueue of mapping.medium_value_queues) {
|
|
const normalizedMedium = mediumQueue.toLowerCase().trim();
|
|
if (normalizedQueue.includes(normalizedMedium) || normalizedMedium.includes(normalizedQueue)) {
|
|
return 'medium';
|
|
}
|
|
}
|
|
|
|
// Default: medium (for unmapped queues)
|
|
return 'medium';
|
|
}
|
|
|
|
/**
|
|
* Classifies all unique queues from a set of interactions
|
|
* Returns a map of queue → segment
|
|
*/
|
|
export function classifyAllQueues(
|
|
interactions: RawInteraction[],
|
|
mapping: SegmentMapping
|
|
): Map<string, CustomerSegment> {
|
|
const queueSegments = new Map<string, CustomerSegment>();
|
|
|
|
// Get unique queues
|
|
const uniqueQueues = [...new Set(interactions.map(i => i.queue_skill))];
|
|
|
|
// Classify each queue
|
|
uniqueQueues.forEach(queue => {
|
|
queueSegments.set(queue, classifyQueue(queue, mapping));
|
|
});
|
|
|
|
return queueSegments;
|
|
}
|
|
|
|
/**
|
|
* Generates segmentation statistics
|
|
* Returns count, percentage and list of queues by segment
|
|
*/
|
|
export function getSegmentationStats(
|
|
interactions: RawInteraction[],
|
|
queueSegments: Map<string, CustomerSegment>
|
|
): {
|
|
high: { count: number; percentage: number; queues: string[] };
|
|
medium: { count: number; percentage: number; queues: string[] };
|
|
low: { count: number; percentage: number; queues: string[] };
|
|
total: number;
|
|
} {
|
|
const stats = {
|
|
high: { count: 0, percentage: 0, queues: [] as string[] },
|
|
medium: { count: 0, percentage: 0, queues: [] as string[] },
|
|
low: { count: 0, percentage: 0, queues: [] as string[] },
|
|
total: interactions.length
|
|
};
|
|
|
|
// Count interactions by segment
|
|
interactions.forEach(interaction => {
|
|
const segment = queueSegments.get(interaction.queue_skill) || 'medium';
|
|
stats[segment].count++;
|
|
});
|
|
|
|
// Calculate percentages
|
|
const total = interactions.length;
|
|
if (total > 0) {
|
|
stats.high.percentage = Math.round((stats.high.count / total) * 100);
|
|
stats.medium.percentage = Math.round((stats.medium.count / total) * 100);
|
|
stats.low.percentage = Math.round((stats.low.count / total) * 100);
|
|
}
|
|
|
|
// Get queues by segment (unique)
|
|
queueSegments.forEach((segment, queue) => {
|
|
if (!stats[segment].queues.includes(queue)) {
|
|
stats[segment].queues.push(queue);
|
|
}
|
|
});
|
|
|
|
return stats;
|
|
}
|
|
|
|
/**
|
|
* Validates that the mapping has at least one queue in some segment
|
|
*/
|
|
export function isValidMapping(mapping: SegmentMapping): boolean {
|
|
return (
|
|
mapping.high_value_queues.length > 0 ||
|
|
mapping.medium_value_queues.length > 0 ||
|
|
mapping.low_value_queues.length > 0
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates a mapping from StaticConfig
|
|
* If there is no segment_mapping, returns empty mapping
|
|
*/
|
|
export function getMappingFromConfig(config: StaticConfig): SegmentMapping | null {
|
|
if (!config.segment_mapping) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
high_value_queues: config.segment_mapping.high_value_queues || [],
|
|
medium_value_queues: config.segment_mapping.medium_value_queues || [],
|
|
low_value_queues: config.segment_mapping.low_value_queues || []
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gets the segment for a specific queue from the config
|
|
* If there is no mapping, returns 'medium' by default
|
|
*/
|
|
export function getSegmentForQueue(
|
|
queue: string,
|
|
config: StaticConfig
|
|
): CustomerSegment {
|
|
const mapping = getMappingFromConfig(config);
|
|
|
|
if (!mapping || !isValidMapping(mapping)) {
|
|
return 'medium';
|
|
}
|
|
|
|
return classifyQueue(queue, mapping);
|
|
}
|
|
|
|
/**
|
|
* Formats statistics for display in UI
|
|
*/
|
|
export function formatSegmentationSummary(
|
|
stats: ReturnType<typeof getSegmentationStats>
|
|
): string {
|
|
const parts: string[] = [];
|
|
|
|
if (stats.high.count > 0) {
|
|
parts.push(`${stats.high.percentage}% High Value (${stats.high.count} interactions)`);
|
|
}
|
|
|
|
if (stats.medium.count > 0) {
|
|
parts.push(`${stats.medium.percentage}% Medium Value (${stats.medium.count} interactions)`);
|
|
}
|
|
|
|
if (stats.low.count > 0) {
|
|
parts.push(`${stats.low.percentage}% Low Value (${stats.low.count} interactions)`);
|
|
}
|
|
|
|
return parts.join(' | ');
|
|
}
|