diff --git a/lib/metrics.ts b/lib/metrics.ts index bdb4826..72841df 100644 --- a/lib/metrics.ts +++ b/lib/metrics.ts @@ -453,41 +453,25 @@ export function sessionMetrics( if (session.escalated) escalatedCount++; if (session.forwardedHr) forwardedHrCount++; - // Sentiment + // Sentiment (now using enum values) if (session.sentiment !== undefined && session.sentiment !== null) { - // Example thresholds, adjust as needed - if (session.sentiment > 0.3) sentimentPositiveCount++; - else if (session.sentiment < -0.3) sentimentNegativeCount++; - else sentimentNeutralCount++; + if (session.sentiment === "POSITIVE") sentimentPositiveCount++; + else if (session.sentiment === "NEGATIVE") sentimentNegativeCount++; + else if (session.sentiment === "NEUTRAL") sentimentNeutralCount++; } - // Sentiment Alert Check + // Sentiment Alert Check (simplified for enum) if ( companyConfig.sentimentAlert !== undefined && - session.sentiment !== undefined && - session.sentiment !== null && - session.sentiment < companyConfig.sentimentAlert + session.sentiment === "NEGATIVE" ) { alerts++; } - // Tokens - if (session.tokens !== undefined && session.tokens !== null) { - totalTokens += session.tokens; - } - if (session.tokensEur !== undefined && session.tokensEur !== null) { - totalTokensEur += session.tokensEur; - } - // Daily metrics const day = new Date(session.startTime).toISOString().split("T")[0]; byDay[day] = (byDay[day] || 0) + 1; // Sessions per day - if (session.tokens !== undefined && session.tokens !== null) { - tokensByDay[day] = (tokensByDay[day] || 0) + session.tokens; - } - if (session.tokensEur !== undefined && session.tokensEur !== null) { - tokensCostByDay[day] = (tokensCostByDay[day] || 0) + session.tokensEur; - } + // Note: tokens and tokensEur are not available in the new schema // Category metrics if (session.category) { @@ -506,24 +490,7 @@ export function sessionMetrics( // Extract questions from session const extractQuestions = () => { - // 1. Extract from questions JSON field - if (session.questions) { - try { - const questionsArray = JSON.parse(session.questions); - if (Array.isArray(questionsArray)) { - questionsArray.forEach((question: string) => { - if (question && question.trim().length > 0) { - const cleanQuestion = question.trim(); - questionCounts[cleanQuestion] = (questionCounts[cleanQuestion] || 0) + 1; - } - }); - } - } catch (error) { - console.warn(`[metrics] Failed to parse questions JSON for session ${session.id}: ${error}`); - } - } - - // 2. Extract questions from user messages (if available) + // 1. Extract questions from user messages (if available) if (session.messages) { session.messages .filter(msg => msg.role === 'User') diff --git a/lib/scheduler.ts b/lib/scheduler.ts index da05fe5..1989fce 100644 --- a/lib/scheduler.ts +++ b/lib/scheduler.ts @@ -52,7 +52,7 @@ export function startCsvImportScheduler() { tokensEur: rawSession.tokensEur, category: rawSession.category, initialMessage: rawSession.initialMessage, - status: "QUEUED", // Reset status for reprocessing if needed + // Status tracking now handled by ProcessingStatusManager }, create: { companyId: company.id, @@ -72,7 +72,7 @@ export function startCsvImportScheduler() { tokensEur: rawSession.tokensEur, category: rawSession.category, initialMessage: rawSession.initialMessage, - status: "QUEUED", + // Status tracking now handled by ProcessingStatusManager }, }); } catch (error) { diff --git a/lib/schedulers.ts b/lib/schedulers.ts index 7e738ac..74bccfb 100644 --- a/lib/schedulers.ts +++ b/lib/schedulers.ts @@ -1,15 +1,15 @@ // Combined scheduler initialization -import { startScheduler } from "./scheduler"; +import { startCsvImportScheduler } from "./scheduler"; import { startProcessingScheduler } from "./processingScheduler"; /** * Initialize all schedulers - * - Session refresh scheduler (runs every 15 minutes) + * - CSV import scheduler (runs every 15 minutes) * - Session processing scheduler (runs every hour) */ export function initializeSchedulers() { - // Start the session refresh scheduler - startScheduler(); + // Start the CSV import scheduler + startCsvImportScheduler(); // Start the session processing scheduler startProcessingScheduler(); diff --git a/lib/types.ts b/lib/types.ts index fe6f890..d9a8af0 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -54,8 +54,7 @@ export interface ChatSession { language?: string | null; country?: string | null; ipAddress?: string | null; - sentiment?: number | null; - sentimentCategory?: string | null; // "positive", "neutral", "negative" from OpenAPI + sentiment?: string | null; // Now a SentimentCategory enum: "POSITIVE", "NEUTRAL", "NEGATIVE" messagesSent?: number; startTime: Date; endTime?: Date | null; @@ -66,14 +65,11 @@ export interface ChatSession { avgResponseTime?: number | null; escalated?: boolean; forwardedHr?: boolean; - tokens?: number; - tokensEur?: number; initialMsg?: string; fullTranscriptUrl?: string | null; - processed?: boolean | null; // Flag for post-processing status - questions?: string | null; // JSON array of questions asked by user summary?: string | null; // Brief summary of the conversation messages?: Message[]; // Parsed messages from transcript + transcriptContent?: string | null; // Full transcript content } export interface SessionQuery { diff --git a/pages/api/admin/refresh-sessions.ts b/pages/api/admin/refresh-sessions.ts index 08a47d6..03d219a 100644 --- a/pages/api/admin/refresh-sessions.ts +++ b/pages/api/admin/refresh-sessions.ts @@ -82,7 +82,7 @@ export default async function handler( tokensEur: rawSession.tokensEur, category: rawSession.category, initialMessage: rawSession.initialMessage, - status: "QUEUED", // Reset status for reprocessing if needed + // Status tracking now handled by ProcessingStatusManager }, create: { companyId: company.id, @@ -102,7 +102,7 @@ export default async function handler( tokensEur: rawSession.tokensEur, category: rawSession.category, initialMessage: rawSession.initialMessage, - status: "QUEUED", + // Status tracking now handled by ProcessingStatusManager }, }); importedCount++; diff --git a/pages/api/admin/trigger-processing.ts b/pages/api/admin/trigger-processing.ts index 9d9e428..9ddc519 100644 --- a/pages/api/admin/trigger-processing.ts +++ b/pages/api/admin/trigger-processing.ts @@ -3,6 +3,8 @@ import { getServerSession } from "next-auth"; import { authOptions } from "../auth/[...nextauth]"; import { prisma } from "../../../lib/prisma"; import { processUnprocessedSessions } from "../../../lib/processingScheduler"; +import { ProcessingStatusManager } from "../../../lib/processingStatusManager"; +import { ProcessingStage } from "@prisma/client"; interface SessionUser { email: string; @@ -53,19 +55,23 @@ export default async function handler( const validatedBatchSize = batchSize && batchSize > 0 ? parseInt(batchSize) : null; const validatedMaxConcurrency = maxConcurrency && maxConcurrency > 0 ? parseInt(maxConcurrency) : 5; - // Check how many unprocessed sessions exist - const unprocessedCount = await prisma.session.count({ - where: { - companyId: user.companyId, - processed: false, - messages: { some: {} }, // Must have messages - }, - }); + // Check how many sessions need AI processing using the new status system + const sessionsNeedingAI = await ProcessingStatusManager.getSessionsNeedingProcessing( + ProcessingStage.AI_ANALYSIS, + 1000 // Get count only + ); + + // Filter to sessions for this company + const companySessionsNeedingAI = sessionsNeedingAI.filter( + statusRecord => statusRecord.session.companyId === user.companyId + ); + + const unprocessedCount = companySessionsNeedingAI.length; if (unprocessedCount === 0) { return res.json({ success: true, - message: "No unprocessed sessions found", + message: "No sessions requiring AI processing found", unprocessedCount: 0, processedCount: 0, }); diff --git a/pages/api/dashboard/metrics.ts b/pages/api/dashboard/metrics.ts index f73a4e5..f8501e2 100644 --- a/pages/api/dashboard/metrics.ts +++ b/pages/api/dashboard/metrics.ts @@ -36,10 +36,9 @@ export default async function handler( // Get date range from query parameters const { startDate, endDate } = req.query; - // Build where clause with optional date filtering and only processed sessions + // Build where clause with optional date filtering const whereClause: any = { companyId: user.companyId, - processed: true, // Only show processed sessions in dashboard }; if (startDate && endDate) { @@ -74,13 +73,10 @@ export default async function handler( messagesSent: ps.messagesSent === null ? undefined : ps.messagesSent, // Handle null messagesSent avgResponseTime: ps.avgResponseTime === null ? undefined : ps.avgResponseTime, - tokens: ps.tokens === null ? undefined : ps.tokens, - tokensEur: ps.tokensEur === null ? undefined : ps.tokensEur, escalated: ps.escalated || false, forwardedHr: ps.forwardedHr || false, initialMsg: ps.initialMsg || undefined, fullTranscriptUrl: ps.fullTranscriptUrl || undefined, - questions: ps.questions || undefined, // Include questions field summary: ps.summary || undefined, // Include summary field messages: ps.messages || [], // Include messages for question extraction // userId is missing in Prisma Session model, assuming it's not strictly needed for metrics or can be null diff --git a/pages/api/dashboard/session/[id].ts b/pages/api/dashboard/session/[id].ts index d34f673..770b1fa 100644 --- a/pages/api/dashboard/session/[id].ts +++ b/pages/api/dashboard/session/[id].ts @@ -51,18 +51,14 @@ export default async function handler( country: prismaSession.country ?? null, ipAddress: prismaSession.ipAddress ?? null, sentiment: prismaSession.sentiment ?? null, - sentimentCategory: prismaSession.sentimentCategory ?? null, // New field messagesSent: prismaSession.messagesSent ?? undefined, // Use undefined if ChatSession expects number | undefined avgResponseTime: prismaSession.avgResponseTime ?? null, escalated: prismaSession.escalated ?? undefined, forwardedHr: prismaSession.forwardedHr ?? undefined, - tokens: prismaSession.tokens ?? undefined, - tokensEur: prismaSession.tokensEur ?? undefined, initialMsg: prismaSession.initialMsg ?? undefined, fullTranscriptUrl: prismaSession.fullTranscriptUrl ?? null, - processed: prismaSession.processed ?? null, // New field - questions: prismaSession.questions ?? null, // New field summary: prismaSession.summary ?? null, // New field + transcriptContent: null, // Not available in Session model messages: prismaSession.messages?.map((msg) => ({ id: msg.id, diff --git a/pages/api/dashboard/sessions.ts b/pages/api/dashboard/sessions.ts index 158f941..01f482e 100644 --- a/pages/api/dashboard/sessions.ts +++ b/pages/api/dashboard/sessions.ts @@ -50,16 +50,16 @@ export default async function handler( ) { const searchConditions = [ { id: { contains: searchTerm } }, - { category: { contains: searchTerm } }, { initialMsg: { contains: searchTerm } }, - { transcriptContent: { contains: searchTerm } }, + { summary: { contains: searchTerm } }, ]; whereClause.OR = searchConditions; } // Category Filter if (category && typeof category === "string" && category.trim() !== "") { - whereClause.category = category; + // Cast to SessionCategory enum if it's a valid value + whereClause.category = category as any; } // Language Filter @@ -146,8 +146,6 @@ export default async function handler( avgResponseTime: ps.avgResponseTime ?? null, escalated: ps.escalated ?? undefined, forwardedHr: ps.forwardedHr ?? undefined, - tokens: ps.tokens ?? undefined, - tokensEur: ps.tokensEur ?? undefined, initialMsg: ps.initialMsg ?? undefined, fullTranscriptUrl: ps.fullTranscriptUrl ?? null, transcriptContent: null, // Transcript content is now fetched from fullTranscriptUrl when needed