mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 10:12:09 +01:00
Enhances dashboard metrics and session handling with word cloud data and country metrics
This commit is contained in:
327
lib/metrics.ts
327
lib/metrics.ts
@ -4,13 +4,308 @@ import {
|
||||
DayMetrics,
|
||||
CategoryMetrics,
|
||||
LanguageMetrics,
|
||||
CountryMetrics, // Added CountryMetrics
|
||||
MetricsResult,
|
||||
WordCloudWord, // Added WordCloudWord
|
||||
} from "./types";
|
||||
|
||||
interface CompanyConfig {
|
||||
sentimentAlert?: number;
|
||||
}
|
||||
|
||||
// List of common stop words - this can be expanded
|
||||
const stopWords = new Set([
|
||||
"assistant",
|
||||
"user",
|
||||
// Web
|
||||
"com",
|
||||
"www",
|
||||
"http",
|
||||
"https",
|
||||
"www2",
|
||||
"href",
|
||||
"html",
|
||||
"php",
|
||||
"js",
|
||||
"css",
|
||||
"xml",
|
||||
"json",
|
||||
"txt",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"png",
|
||||
"gif",
|
||||
"bmp",
|
||||
"svg",
|
||||
"org",
|
||||
"net",
|
||||
"co",
|
||||
"io",
|
||||
// English stop words
|
||||
"a",
|
||||
"an",
|
||||
"the",
|
||||
"is",
|
||||
"are",
|
||||
"was",
|
||||
"were",
|
||||
"be",
|
||||
"been",
|
||||
"being",
|
||||
"have",
|
||||
"has",
|
||||
"had",
|
||||
"do",
|
||||
"does",
|
||||
"did",
|
||||
"will",
|
||||
"would",
|
||||
"should",
|
||||
"can",
|
||||
"could",
|
||||
"may",
|
||||
"might",
|
||||
"must",
|
||||
"am",
|
||||
"i",
|
||||
"you",
|
||||
"he",
|
||||
"she",
|
||||
"it",
|
||||
"we",
|
||||
"they",
|
||||
"me",
|
||||
"him",
|
||||
"her",
|
||||
"us",
|
||||
"them",
|
||||
"my",
|
||||
"your",
|
||||
"his",
|
||||
"its",
|
||||
"our",
|
||||
"their",
|
||||
"mine",
|
||||
"yours",
|
||||
"hers",
|
||||
"ours",
|
||||
"theirs",
|
||||
"to",
|
||||
"of",
|
||||
"in",
|
||||
"on",
|
||||
"at",
|
||||
"by",
|
||||
"for",
|
||||
"with",
|
||||
"about",
|
||||
"against",
|
||||
"between",
|
||||
"into",
|
||||
"through",
|
||||
"during",
|
||||
"before",
|
||||
"after",
|
||||
"above",
|
||||
"below",
|
||||
"from",
|
||||
"up",
|
||||
"down",
|
||||
"out",
|
||||
"off",
|
||||
"over",
|
||||
"under",
|
||||
"again",
|
||||
"further",
|
||||
"then",
|
||||
"once",
|
||||
"here",
|
||||
"there",
|
||||
"when",
|
||||
"where",
|
||||
"why",
|
||||
"how",
|
||||
"all",
|
||||
"any",
|
||||
"both",
|
||||
"each",
|
||||
"few",
|
||||
"more",
|
||||
"most",
|
||||
"other",
|
||||
"some",
|
||||
"such",
|
||||
"no",
|
||||
"nor",
|
||||
"not",
|
||||
"only",
|
||||
"own",
|
||||
"same",
|
||||
"so",
|
||||
"than",
|
||||
"too",
|
||||
"very",
|
||||
"s",
|
||||
"t",
|
||||
"just",
|
||||
"don",
|
||||
"shouldve",
|
||||
"now",
|
||||
"d",
|
||||
"ll",
|
||||
"m",
|
||||
"o",
|
||||
"re",
|
||||
"ve",
|
||||
"y",
|
||||
"ain",
|
||||
"aren",
|
||||
"couldn",
|
||||
"didn",
|
||||
"doesn",
|
||||
"hadn",
|
||||
"hasn",
|
||||
"haven",
|
||||
"isn",
|
||||
"ma",
|
||||
"mightn",
|
||||
"mustn",
|
||||
"needn",
|
||||
"shan",
|
||||
"shouldn",
|
||||
"wasn",
|
||||
"weren",
|
||||
"won",
|
||||
"wouldn",
|
||||
"hi",
|
||||
"hello",
|
||||
"thanks",
|
||||
"thank",
|
||||
"please",
|
||||
"ok",
|
||||
"okay",
|
||||
"yes",
|
||||
"yeah",
|
||||
"bye",
|
||||
"goodbye",
|
||||
// French stop words
|
||||
"la",
|
||||
"le",
|
||||
"les",
|
||||
"un",
|
||||
"une",
|
||||
"des",
|
||||
"et",
|
||||
"ou",
|
||||
"mais",
|
||||
"donc",
|
||||
// Dutch stop words
|
||||
"dit",
|
||||
"ben",
|
||||
"de",
|
||||
"het",
|
||||
"ik",
|
||||
"jij",
|
||||
"hij",
|
||||
"zij",
|
||||
"wij",
|
||||
"jullie",
|
||||
"deze",
|
||||
"dit",
|
||||
"dat",
|
||||
"die",
|
||||
"een",
|
||||
"en",
|
||||
"of",
|
||||
"maar",
|
||||
"want",
|
||||
"omdat",
|
||||
"dus",
|
||||
"als",
|
||||
"ook",
|
||||
"dan",
|
||||
"nu",
|
||||
"nog",
|
||||
"al",
|
||||
"naar",
|
||||
"voor",
|
||||
"van",
|
||||
"door",
|
||||
"met",
|
||||
"bij",
|
||||
"tot",
|
||||
"om",
|
||||
"over",
|
||||
"tussen",
|
||||
"onder",
|
||||
"boven",
|
||||
"tegen",
|
||||
"aan",
|
||||
"uit",
|
||||
"sinds",
|
||||
"tijdens",
|
||||
"binnen",
|
||||
"buiten",
|
||||
"zonder",
|
||||
"volgens",
|
||||
"dankzij",
|
||||
"ondanks",
|
||||
"behalve",
|
||||
"mits",
|
||||
"tenzij",
|
||||
"hoewel",
|
||||
"alhoewel",
|
||||
"toch",
|
||||
"anders",
|
||||
"echter",
|
||||
"wel",
|
||||
"niet",
|
||||
"geen",
|
||||
"iets",
|
||||
"niets",
|
||||
"veel",
|
||||
"weinig",
|
||||
"meer",
|
||||
"meest",
|
||||
"elk",
|
||||
"ieder",
|
||||
"sommige",
|
||||
"hoe",
|
||||
"wat",
|
||||
"waar",
|
||||
"wie",
|
||||
"wanneer",
|
||||
"waarom",
|
||||
"welke",
|
||||
"wordt",
|
||||
"worden",
|
||||
"werd",
|
||||
"werden",
|
||||
"geworden",
|
||||
"zijn",
|
||||
"ben",
|
||||
"bent",
|
||||
"was",
|
||||
"waren",
|
||||
"geweest",
|
||||
"hebben",
|
||||
"heb",
|
||||
"hebt",
|
||||
"heeft",
|
||||
"had",
|
||||
"hadden",
|
||||
"gehad",
|
||||
"kunnen",
|
||||
"kan",
|
||||
"kunt",
|
||||
"kon",
|
||||
"konden",
|
||||
"zullen",
|
||||
"zal",
|
||||
"zult",
|
||||
// Add more domain-specific stop words if necessary
|
||||
]);
|
||||
|
||||
export function sessionMetrics(
|
||||
sessions: ChatSession[],
|
||||
companyConfig: CompanyConfig = {}
|
||||
@ -19,6 +314,7 @@ export function sessionMetrics(
|
||||
const byDay: DayMetrics = {};
|
||||
const byCategory: CategoryMetrics = {};
|
||||
const byLanguage: LanguageMetrics = {};
|
||||
const byCountry: CountryMetrics = {}; // Added for country data
|
||||
const tokensByDay: DayMetrics = {};
|
||||
const tokensCostByDay: DayMetrics = {};
|
||||
|
||||
@ -40,12 +336,15 @@ export function sessionMetrics(
|
||||
let totalDuration = 0;
|
||||
let durationCount = 0;
|
||||
|
||||
const wordCounts: { [key: string]: number } = {}; // For WordCloud
|
||||
|
||||
sessions.forEach((s) => {
|
||||
const day = s.startTime.toISOString().slice(0, 10);
|
||||
byDay[day] = (byDay[day] || 0) + 1;
|
||||
|
||||
if (s.category) byCategory[s.category] = (byCategory[s.category] || 0) + 1;
|
||||
if (s.language) byLanguage[s.language] = (byLanguage[s.language] || 0) + 1;
|
||||
if (s.country) byCountry[s.country] = (byCountry[s.country] || 0) + 1; // Populate byCountry
|
||||
|
||||
// Process token usage by day
|
||||
if (s.tokens) {
|
||||
@ -88,6 +387,24 @@ export function sessionMetrics(
|
||||
|
||||
totalTokens += s.tokens || 0;
|
||||
totalTokensEur += s.tokensEur || 0;
|
||||
|
||||
// Process transcript for WordCloud
|
||||
if (s.transcriptContent) {
|
||||
const words = s.transcriptContent.toLowerCase().match(/\b\w+\b/g); // Split into words, lowercase
|
||||
if (words) {
|
||||
words.forEach((word) => {
|
||||
const cleanedWord = word.replace(/[^a-z0-9]/gi, ""); // Remove punctuation
|
||||
if (
|
||||
cleanedWord &&
|
||||
!stopWords.has(cleanedWord) &&
|
||||
cleanedWord.length > 2
|
||||
) {
|
||||
// Check if not a stop word and length > 2
|
||||
wordCounts[cleanedWord] = (wordCounts[cleanedWord] || 0) + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Now add sentiment alert logic:
|
||||
@ -107,13 +424,20 @@ export function sessionMetrics(
|
||||
const avgSessionLength =
|
||||
durationCount > 0 ? totalDuration / durationCount : null;
|
||||
|
||||
// Prepare wordCloudData
|
||||
const wordCloudData: WordCloudWord[] = Object.entries(wordCounts)
|
||||
.map(([text, value]) => ({ text, value }))
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.slice(0, 500); // Take top 500 words
|
||||
|
||||
return {
|
||||
totalSessions: total,
|
||||
avgSessionsPerDay,
|
||||
avgSessionLength,
|
||||
days: byDay,
|
||||
languages: byLanguage,
|
||||
categories: byCategory,
|
||||
categories: byCategory, // This will be empty if we are not using categories for word cloud
|
||||
countries: byCountry, // Added countries to the result
|
||||
belowThresholdCount: belowThreshold,
|
||||
// Additional metrics not in the interface - using type assertion
|
||||
escalatedCount: escalated,
|
||||
@ -131,5 +455,6 @@ export function sessionMetrics(
|
||||
sentimentNegativeCount: sentimentNegative,
|
||||
tokensByDay,
|
||||
tokensCostByDay,
|
||||
wordCloudData, // Added word cloud data
|
||||
};
|
||||
}
|
||||
|
||||
11
lib/types.ts
11
lib/types.ts
@ -74,6 +74,15 @@ export interface LanguageMetrics {
|
||||
[language: string]: number;
|
||||
}
|
||||
|
||||
export interface CountryMetrics {
|
||||
[country: string]: number;
|
||||
}
|
||||
|
||||
export interface WordCloudWord {
|
||||
text: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface MetricsResult {
|
||||
totalSessions: number;
|
||||
avgSessionsPerDay: number;
|
||||
@ -81,6 +90,7 @@ export interface MetricsResult {
|
||||
days: DayMetrics;
|
||||
languages: LanguageMetrics;
|
||||
categories: CategoryMetrics;
|
||||
countries: CountryMetrics; // Added for geographic distribution
|
||||
belowThresholdCount: number;
|
||||
// Additional properties for dashboard
|
||||
escalatedCount?: number;
|
||||
@ -98,6 +108,7 @@ export interface MetricsResult {
|
||||
sentimentNegativeCount?: number;
|
||||
tokensByDay?: DayMetrics;
|
||||
tokensCostByDay?: DayMetrics;
|
||||
wordCloudData?: WordCloudWord[]; // Added for transcript-based word cloud
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
|
||||
Reference in New Issue
Block a user