mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 18:52:08 +01:00
Broken shit
This commit is contained in:
64
scripts/check-database-status.js
Normal file
64
scripts/check-database-status.js
Normal file
@ -0,0 +1,64 @@
|
||||
// Check current database status
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function checkDatabaseStatus() {
|
||||
try {
|
||||
console.log('📊 Checking database status...\n');
|
||||
|
||||
// Count total sessions
|
||||
const totalSessions = await prisma.session.count();
|
||||
console.log(`📈 Total sessions: ${totalSessions}`);
|
||||
|
||||
// Count processed vs unprocessed
|
||||
const processedSessions = await prisma.session.count({
|
||||
where: { processed: true }
|
||||
});
|
||||
const unprocessedSessions = await prisma.session.count({
|
||||
where: { processed: false }
|
||||
});
|
||||
|
||||
console.log(`✅ Processed sessions: ${processedSessions}`);
|
||||
console.log(`⏳ Unprocessed sessions: ${unprocessedSessions}`);
|
||||
|
||||
// Count valid vs invalid data
|
||||
const validSessions = await prisma.session.count({
|
||||
where: { validData: true }
|
||||
});
|
||||
const invalidSessions = await prisma.session.count({
|
||||
where: { validData: false }
|
||||
});
|
||||
|
||||
console.log(`🎯 Valid data sessions: ${validSessions}`);
|
||||
console.log(`❌ Invalid data sessions: ${invalidSessions}`);
|
||||
|
||||
// Count sessions with messages
|
||||
const sessionsWithMessages = await prisma.session.count({
|
||||
where: {
|
||||
messages: {
|
||||
some: {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`💬 Sessions with messages: ${sessionsWithMessages}`);
|
||||
|
||||
// Count companies
|
||||
const totalCompanies = await prisma.company.count();
|
||||
console.log(`🏢 Total companies: ${totalCompanies}`);
|
||||
|
||||
if (totalSessions === 0) {
|
||||
console.log('\n💡 No sessions found. Run CSV refresh to import data:');
|
||||
console.log(' curl -X POST http://localhost:3000/api/admin/refresh-sessions');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error checking database status:', error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
checkDatabaseStatus();
|
||||
69
scripts/check-questions-issue.js
Normal file
69
scripts/check-questions-issue.js
Normal file
@ -0,0 +1,69 @@
|
||||
// Check why questions aren't being extracted properly
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function checkQuestionsIssue() {
|
||||
console.log('🔍 INVESTIGATING QUESTIONS EXTRACTION ISSUE\n');
|
||||
|
||||
// Find a session with questions stored
|
||||
const sessionWithQuestions = await prisma.session.findFirst({
|
||||
where: {
|
||||
processed: true,
|
||||
questions: { not: null }
|
||||
},
|
||||
include: { messages: true }
|
||||
});
|
||||
|
||||
if (sessionWithQuestions) {
|
||||
console.log('📋 SAMPLE SESSION WITH QUESTIONS:');
|
||||
console.log('Session ID:', sessionWithQuestions.id);
|
||||
console.log('Questions stored:', sessionWithQuestions.questions);
|
||||
console.log('Summary:', sessionWithQuestions.summary);
|
||||
console.log('Messages count:', sessionWithQuestions.messages.length);
|
||||
|
||||
console.log('\n💬 FIRST FEW MESSAGES:');
|
||||
sessionWithQuestions.messages.slice(0, 8).forEach((msg, i) => {
|
||||
console.log(` ${i+1}. [${msg.role}]: ${msg.content.substring(0, 150)}...`);
|
||||
});
|
||||
}
|
||||
|
||||
// Check sessions marked as invalid data
|
||||
const invalidSessions = await prisma.session.count({
|
||||
where: {
|
||||
processed: true,
|
||||
questions: '[]' // Empty questions array
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\n⚠️ SESSIONS WITH EMPTY QUESTIONS: ${invalidSessions}`);
|
||||
|
||||
// Find a session with empty questions to analyze
|
||||
const emptyQuestionSession = await prisma.session.findFirst({
|
||||
where: {
|
||||
processed: true,
|
||||
questions: '[]'
|
||||
},
|
||||
include: { messages: true }
|
||||
});
|
||||
|
||||
if (emptyQuestionSession) {
|
||||
console.log('\n❌ SAMPLE SESSION WITH EMPTY QUESTIONS:');
|
||||
console.log('Session ID:', emptyQuestionSession.id);
|
||||
console.log('Questions stored:', emptyQuestionSession.questions);
|
||||
console.log('Summary:', emptyQuestionSession.summary);
|
||||
console.log('Messages count:', emptyQuestionSession.messages.length);
|
||||
|
||||
console.log('\n💬 MESSAGES FROM EMPTY QUESTION SESSION:');
|
||||
emptyQuestionSession.messages.slice(0, 8).forEach((msg, i) => {
|
||||
console.log(` ${i+1}. [${msg.role}]: ${msg.content.substring(0, 150)}...`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n🤖 CURRENT OPENAI MODEL: gpt-4-turbo');
|
||||
console.log('🎯 PROMPT INSTRUCTION: "Max 5 user questions in English"');
|
||||
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
checkQuestionsIssue();
|
||||
@ -1,8 +1,8 @@
|
||||
// Script to check what's in the transcript files
|
||||
// Usage: node scripts/check-transcript-content.js
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import fetch from 'node-fetch';
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -11,10 +11,7 @@ async function checkTranscriptContent() {
|
||||
// Get a few sessions without messages
|
||||
const sessions = await prisma.session.findMany({
|
||||
where: {
|
||||
AND: [
|
||||
{ fullTranscriptUrl: { not: null } },
|
||||
{ messages: { none: {} } },
|
||||
]
|
||||
AND: [{ fullTranscriptUrl: { not: null } }, { messages: { none: {} } }],
|
||||
},
|
||||
include: { company: true },
|
||||
take: 3,
|
||||
@ -25,9 +22,13 @@ async function checkTranscriptContent() {
|
||||
console.log(` URL: ${session.fullTranscriptUrl}`);
|
||||
|
||||
try {
|
||||
const authHeader = session.company.csvUsername && session.company.csvPassword
|
||||
? "Basic " + Buffer.from(`${session.company.csvUsername}:${session.company.csvPassword}`).toString("base64")
|
||||
: undefined;
|
||||
const authHeader =
|
||||
session.company.csvUsername && session.company.csvPassword
|
||||
? "Basic " +
|
||||
Buffer.from(
|
||||
`${session.company.csvUsername}:${session.company.csvPassword}`
|
||||
).toString("base64")
|
||||
: undefined;
|
||||
|
||||
const response = await fetch(session.fullTranscriptUrl, {
|
||||
headers: authHeader ? { Authorization: authHeader } : {},
|
||||
@ -47,24 +48,26 @@ async function checkTranscriptContent() {
|
||||
} else if (content.length < 100) {
|
||||
console.log(` 📝 Full content: "${content}"`);
|
||||
} else {
|
||||
console.log(` 📝 First 200 chars: "${content.substring(0, 200)}..."`);
|
||||
console.log(
|
||||
` 📝 First 200 chars: "${content.substring(0, 200)}..."`
|
||||
);
|
||||
}
|
||||
|
||||
// Check if it matches our expected format
|
||||
const lines = content.split('\n').filter(line => line.trim());
|
||||
const formatMatches = lines.filter(line =>
|
||||
const lines = content.split("\n").filter((line) => line.trim());
|
||||
const formatMatches = lines.filter((line) =>
|
||||
line.match(/^\[([^\]]+)\]\s*([^:]+):\s*(.+)$/)
|
||||
);
|
||||
|
||||
console.log(` 🔍 Lines total: ${lines.length}, Format matches: ${formatMatches.length}`);
|
||||
|
||||
console.log(
|
||||
` 🔍 Lines total: ${lines.length}, Format matches: ${formatMatches.length}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error);
|
||||
console.error("❌ Error:", error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
34
scripts/check-transcript-urls.js
Normal file
34
scripts/check-transcript-urls.js
Normal file
@ -0,0 +1,34 @@
|
||||
// Check sessions for transcript URLs
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function checkTranscriptUrls() {
|
||||
const sessions = await prisma.session.findMany({
|
||||
where: {
|
||||
messages: { none: {} },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
fullTranscriptUrl: true,
|
||||
}
|
||||
});
|
||||
|
||||
const withUrl = sessions.filter(s => s.fullTranscriptUrl);
|
||||
const withoutUrl = sessions.filter(s => !s.fullTranscriptUrl);
|
||||
|
||||
console.log(`\n📊 Transcript URL Status for Sessions without Messages:`);
|
||||
console.log(`✅ Sessions with transcript URL: ${withUrl.length}`);
|
||||
console.log(`❌ Sessions without transcript URL: ${withoutUrl.length}`);
|
||||
|
||||
if (withUrl.length > 0) {
|
||||
console.log(`\n🔍 Sample URLs:`);
|
||||
withUrl.slice(0, 3).forEach(s => {
|
||||
console.log(` ${s.id}: ${s.fullTranscriptUrl}`);
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
checkTranscriptUrls();
|
||||
144
scripts/complete-processing-workflow.js
Normal file
144
scripts/complete-processing-workflow.js
Normal file
@ -0,0 +1,144 @@
|
||||
// Complete processing workflow - Fetches transcripts AND processes everything
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { processUnprocessedSessions } from '../lib/processingScheduler.ts';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function completeProcessingWorkflow() {
|
||||
try {
|
||||
console.log('🚀 COMPLETE PROCESSING WORKFLOW STARTED\n');
|
||||
|
||||
// Step 1: Check initial status
|
||||
console.log('📊 STEP 1: Initial Status Check');
|
||||
console.log('=' .repeat(50));
|
||||
await checkStatus();
|
||||
|
||||
// Step 2: Fetch missing transcripts
|
||||
console.log('\n📥 STEP 2: Fetching Missing Transcripts');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
const sessionsWithoutMessages = await prisma.session.count({
|
||||
where: {
|
||||
messages: { none: {} },
|
||||
fullTranscriptUrl: { not: null }
|
||||
}
|
||||
});
|
||||
|
||||
if (sessionsWithoutMessages > 0) {
|
||||
console.log(`🔍 Found ${sessionsWithoutMessages} sessions needing transcript fetch`);
|
||||
console.log('📥 Fetching transcripts...\n');
|
||||
|
||||
try {
|
||||
const { stdout } = await execAsync('node scripts/fetch-and-parse-transcripts.js');
|
||||
console.log(stdout);
|
||||
} catch (error) {
|
||||
console.error('❌ Error fetching transcripts:', error);
|
||||
}
|
||||
} else {
|
||||
console.log('✅ All sessions with transcript URLs already have messages');
|
||||
}
|
||||
|
||||
// Step 3: Process ALL unprocessed sessions
|
||||
console.log('\n🤖 STEP 3: AI Processing (Complete Batch Processing)');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
const unprocessedWithMessages = await prisma.session.count({
|
||||
where: {
|
||||
processed: false,
|
||||
messages: { some: {} }
|
||||
}
|
||||
});
|
||||
|
||||
if (unprocessedWithMessages > 0) {
|
||||
console.log(`🔄 Found ${unprocessedWithMessages} unprocessed sessions with messages`);
|
||||
console.log('🤖 Starting complete batch processing...\n');
|
||||
|
||||
const result = await processUnprocessedSessions(10, 3);
|
||||
|
||||
console.log('\n🎉 AI Processing Results:');
|
||||
console.log(` ✅ Successfully processed: ${result.totalProcessed}`);
|
||||
console.log(` ❌ Failed to process: ${result.totalFailed}`);
|
||||
console.log(` ⏱️ Total time: ${result.totalTime.toFixed(2)}s`);
|
||||
} else {
|
||||
console.log('✅ No unprocessed sessions with messages found');
|
||||
}
|
||||
|
||||
// Step 4: Continue fetching more transcripts if available
|
||||
console.log('\n🔄 STEP 4: Checking for More Transcripts');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
const remainingWithoutMessages = await prisma.session.count({
|
||||
where: {
|
||||
messages: { none: {} },
|
||||
fullTranscriptUrl: { not: null }
|
||||
}
|
||||
});
|
||||
|
||||
if (remainingWithoutMessages > 0) {
|
||||
console.log(`🔍 Found ${remainingWithoutMessages} more sessions needing transcripts`);
|
||||
console.log('📥 Fetching additional transcripts...\n');
|
||||
|
||||
try {
|
||||
const { stdout } = await execAsync('node scripts/fetch-and-parse-transcripts.js');
|
||||
console.log(stdout);
|
||||
|
||||
// Process the newly fetched sessions
|
||||
const newUnprocessed = await prisma.session.count({
|
||||
where: {
|
||||
processed: false,
|
||||
messages: { some: {} }
|
||||
}
|
||||
});
|
||||
|
||||
if (newUnprocessed > 0) {
|
||||
console.log(`\n🤖 Processing ${newUnprocessed} newly fetched sessions...\n`);
|
||||
const result = await processUnprocessedSessions(10, 3);
|
||||
console.log(`✅ Additional processing: ${result.totalProcessed} processed, ${result.totalFailed} failed`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error fetching additional transcripts:', error);
|
||||
}
|
||||
} else {
|
||||
console.log('✅ No more sessions need transcript fetching');
|
||||
}
|
||||
|
||||
// Step 5: Final status
|
||||
console.log('\n📊 STEP 5: Final Status');
|
||||
console.log('=' .repeat(50));
|
||||
await checkStatus();
|
||||
|
||||
console.log('\n🎯 WORKFLOW COMPLETE!');
|
||||
console.log('✅ All available sessions have been processed');
|
||||
console.log('✅ System ready for new data');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error in complete workflow:', error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
async function checkStatus() {
|
||||
const totalSessions = await prisma.session.count();
|
||||
const processedSessions = await prisma.session.count({ where: { processed: true } });
|
||||
const unprocessedSessions = await prisma.session.count({ where: { processed: false } });
|
||||
const sessionsWithMessages = await prisma.session.count({
|
||||
where: { messages: { some: {} } }
|
||||
});
|
||||
const sessionsWithoutMessages = await prisma.session.count({
|
||||
where: { messages: { none: {} } }
|
||||
});
|
||||
|
||||
console.log(`📈 Total sessions: ${totalSessions}`);
|
||||
console.log(`✅ Processed sessions: ${processedSessions}`);
|
||||
console.log(`⏳ Unprocessed sessions: ${unprocessedSessions}`);
|
||||
console.log(`💬 Sessions with messages: ${sessionsWithMessages}`);
|
||||
console.log(`📄 Sessions without messages: ${sessionsWithoutMessages}`);
|
||||
}
|
||||
|
||||
// Run the complete workflow
|
||||
completeProcessingWorkflow();
|
||||
99
scripts/complete-workflow-demo.js
Normal file
99
scripts/complete-workflow-demo.js
Normal file
@ -0,0 +1,99 @@
|
||||
// Complete workflow demonstration - Shows the full automated processing system
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { processUnprocessedSessions } from '../lib/processingScheduler.ts';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function demonstrateCompleteWorkflow() {
|
||||
try {
|
||||
console.log('🚀 COMPLETE AUTOMATED WORKFLOW DEMONSTRATION\n');
|
||||
|
||||
// Step 1: Check initial status
|
||||
console.log('📊 STEP 1: Initial Database Status');
|
||||
console.log('=' .repeat(50));
|
||||
await checkDatabaseStatus();
|
||||
|
||||
// Step 2: Fetch any missing transcripts
|
||||
console.log('\n📥 STEP 2: Fetching Missing Transcripts');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
const sessionsWithoutMessages = await prisma.session.count({
|
||||
where: {
|
||||
messages: { none: {} },
|
||||
fullTranscriptUrl: { not: null }
|
||||
}
|
||||
});
|
||||
|
||||
if (sessionsWithoutMessages > 0) {
|
||||
console.log(`Found ${sessionsWithoutMessages} sessions without messages but with transcript URLs`);
|
||||
console.log('💡 Run: node scripts/fetch-and-parse-transcripts.js');
|
||||
} else {
|
||||
console.log('✅ All sessions with transcript URLs already have messages');
|
||||
}
|
||||
|
||||
// Step 3: Process all unprocessed sessions
|
||||
console.log('\n🤖 STEP 3: Complete AI Processing (All Unprocessed Sessions)');
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
const unprocessedCount = await prisma.session.count({
|
||||
where: {
|
||||
processed: false,
|
||||
messages: { some: {} }
|
||||
}
|
||||
});
|
||||
|
||||
if (unprocessedCount > 0) {
|
||||
console.log(`Found ${unprocessedCount} unprocessed sessions with messages`);
|
||||
console.log('🔄 Starting complete batch processing...\n');
|
||||
|
||||
const result = await processUnprocessedSessions(10, 3);
|
||||
|
||||
console.log('\n🎉 Processing Results:');
|
||||
console.log(` ✅ Successfully processed: ${result.totalProcessed}`);
|
||||
console.log(` ❌ Failed to process: ${result.totalFailed}`);
|
||||
console.log(` ⏱️ Total time: ${result.totalTime.toFixed(2)}s`);
|
||||
} else {
|
||||
console.log('✅ No unprocessed sessions found - all caught up!');
|
||||
}
|
||||
|
||||
// Step 4: Final status
|
||||
console.log('\n📊 STEP 4: Final Database Status');
|
||||
console.log('=' .repeat(50));
|
||||
await checkDatabaseStatus();
|
||||
|
||||
// Step 5: System summary
|
||||
console.log('\n🎯 STEP 5: Automated System Summary');
|
||||
console.log('=' .repeat(50));
|
||||
console.log('✅ HOURLY SCHEDULER: Processes new unprocessed sessions automatically');
|
||||
console.log('✅ DASHBOARD REFRESH: Triggers processing when refresh button is pressed');
|
||||
console.log('✅ BATCH PROCESSING: Processes ALL unprocessed sessions until completion');
|
||||
console.log('✅ QUALITY VALIDATION: Filters out low-quality sessions automatically');
|
||||
console.log('✅ COMPLETE AUTOMATION: No manual intervention needed for ongoing operations');
|
||||
|
||||
console.log('\n🚀 SYSTEM READY FOR PRODUCTION!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error in workflow demonstration:', error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
async function checkDatabaseStatus() {
|
||||
const totalSessions = await prisma.session.count();
|
||||
const processedSessions = await prisma.session.count({ where: { processed: true } });
|
||||
const unprocessedSessions = await prisma.session.count({ where: { processed: false } });
|
||||
const sessionsWithMessages = await prisma.session.count({
|
||||
where: { messages: { some: {} } }
|
||||
});
|
||||
const companies = await prisma.company.count();
|
||||
|
||||
console.log(`📈 Total sessions: ${totalSessions}`);
|
||||
console.log(`✅ Processed sessions: ${processedSessions}`);
|
||||
console.log(`⏳ Unprocessed sessions: ${unprocessedSessions}`);
|
||||
console.log(`💬 Sessions with messages: ${sessionsWithMessages}`);
|
||||
console.log(`🏢 Total companies: ${companies}`);
|
||||
}
|
||||
|
||||
// Run the demonstration
|
||||
demonstrateCompleteWorkflow();
|
||||
53
scripts/create-admin-user.js
Normal file
53
scripts/create-admin-user.js
Normal file
@ -0,0 +1,53 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function createAdminUser() {
|
||||
try {
|
||||
// Check if user exists
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: { email: 'admin@example.com' }
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
console.log('✅ User already exists:', existingUser.email);
|
||||
console.log('Password hash:', existingUser.password);
|
||||
return;
|
||||
}
|
||||
|
||||
// First, ensure we have a company
|
||||
let company = await prisma.company.findFirst();
|
||||
if (!company) {
|
||||
company = await prisma.company.create({
|
||||
data: {
|
||||
name: 'Demo Company',
|
||||
csvUrl: 'https://example.com/demo.csv',
|
||||
}
|
||||
});
|
||||
console.log('✅ Created demo company:', company.name);
|
||||
}
|
||||
|
||||
// Create user
|
||||
const hashedPassword = await bcrypt.hash('admin123', 10);
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: 'admin@example.com',
|
||||
password: hashedPassword,
|
||||
role: 'admin',
|
||||
companyId: company.id,
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ User created successfully:', user.email);
|
||||
console.log('Password hash:', user.password);
|
||||
console.log('Role:', user.role);
|
||||
console.log('Company:', company.name);
|
||||
} catch (error) {
|
||||
console.error('❌ Error creating user:', error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
createAdminUser();
|
||||
@ -1,8 +1,8 @@
|
||||
// Script to fetch transcripts and parse them into messages
|
||||
// Usage: node scripts/fetch-and-parse-transcripts.js
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import fetch from 'node-fetch';
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -11,9 +11,10 @@ const prisma = new PrismaClient();
|
||||
*/
|
||||
async function fetchTranscriptContent(url, username, password) {
|
||||
try {
|
||||
const authHeader = username && password
|
||||
? "Basic " + Buffer.from(`${username}:${password}`).toString("base64")
|
||||
: undefined;
|
||||
const authHeader =
|
||||
username && password
|
||||
? "Basic " + Buffer.from(`${username}:${password}`).toString("base64")
|
||||
: undefined;
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: authHeader ? { Authorization: authHeader } : {},
|
||||
@ -21,7 +22,9 @@ async function fetchTranscriptContent(url, username, password) {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.log(`❌ Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
||||
console.log(
|
||||
`❌ Failed to fetch ${url}: ${response.status} ${response.statusText}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return await response.text();
|
||||
@ -35,11 +38,11 @@ async function fetchTranscriptContent(url, username, password) {
|
||||
* Parses transcript content into messages
|
||||
*/
|
||||
function parseTranscriptToMessages(transcript, sessionId) {
|
||||
if (!transcript || transcript.trim() === '') {
|
||||
if (!transcript || transcript.trim() === "") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const lines = transcript.split('\n').filter(line => line.trim());
|
||||
const lines = transcript.split("\n").filter((line) => line.trim());
|
||||
const messages = [];
|
||||
let messageOrder = 0;
|
||||
let currentTimestamp = new Date();
|
||||
@ -52,7 +55,9 @@ function parseTranscriptToMessages(transcript, sessionId) {
|
||||
const [, timestamp, role, content] = timestampMatch;
|
||||
|
||||
// Parse timestamp (DD-MM-YYYY HH:MM:SS)
|
||||
const dateMatch = timestamp.match(/^(\d{1,2})-(\d{1,2})-(\d{4}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/);
|
||||
const dateMatch = timestamp.match(
|
||||
/^(\d{1,2})-(\d{1,2})-(\d{4}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/
|
||||
);
|
||||
let parsedTimestamp = new Date();
|
||||
|
||||
if (dateMatch) {
|
||||
@ -104,7 +109,7 @@ function parseTranscriptToMessages(transcript, sessionId) {
|
||||
*/
|
||||
async function fetchAndParseTranscripts() {
|
||||
try {
|
||||
console.log('🔍 Finding sessions without messages...\n');
|
||||
console.log("🔍 Finding sessions without messages...\n");
|
||||
|
||||
// Get sessions that have fullTranscriptUrl but no messages
|
||||
const sessionsWithoutMessages = await prisma.session.findMany({
|
||||
@ -112,7 +117,7 @@ async function fetchAndParseTranscripts() {
|
||||
AND: [
|
||||
{ fullTranscriptUrl: { not: null } },
|
||||
{ messages: { none: {} } }, // No messages
|
||||
]
|
||||
],
|
||||
},
|
||||
include: {
|
||||
company: true,
|
||||
@ -121,11 +126,15 @@ async function fetchAndParseTranscripts() {
|
||||
});
|
||||
|
||||
if (sessionsWithoutMessages.length === 0) {
|
||||
console.log('✅ All sessions with transcript URLs already have messages!');
|
||||
console.log(
|
||||
"✅ All sessions with transcript URLs already have messages!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📥 Found ${sessionsWithoutMessages.length} sessions to process\n`);
|
||||
console.log(
|
||||
`📥 Found ${sessionsWithoutMessages.length} sessions to process\n`
|
||||
);
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
@ -148,7 +157,10 @@ async function fetchAndParseTranscripts() {
|
||||
}
|
||||
|
||||
// Parse transcript into messages
|
||||
const messages = parseTranscriptToMessages(transcriptContent, session.id);
|
||||
const messages = parseTranscriptToMessages(
|
||||
transcriptContent,
|
||||
session.id
|
||||
);
|
||||
|
||||
if (messages.length === 0) {
|
||||
console.log(` ⚠️ No messages found in transcript`);
|
||||
@ -163,7 +175,6 @@ async function fetchAndParseTranscripts() {
|
||||
|
||||
console.log(` ✅ Added ${messages.length} messages`);
|
||||
successCount++;
|
||||
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error: ${error.message}`);
|
||||
errorCount++;
|
||||
@ -173,10 +184,11 @@ async function fetchAndParseTranscripts() {
|
||||
console.log(`\n📊 Results:`);
|
||||
console.log(` ✅ Successfully processed: ${successCount} sessions`);
|
||||
console.log(` ❌ Failed to process: ${errorCount} sessions`);
|
||||
console.log(`\n💡 Now you can run the processing scheduler to analyze these sessions!`);
|
||||
|
||||
console.log(
|
||||
`\n💡 Now you can run the processing scheduler to analyze these sessions!`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error);
|
||||
console.error("❌ Error:", error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
@ -1,37 +1,39 @@
|
||||
// Simple script to test the manual processing trigger
|
||||
// Usage: node scripts/manual-trigger-test.js
|
||||
|
||||
import fetch from 'node-fetch';
|
||||
import fetch from "node-fetch";
|
||||
|
||||
async function testManualTrigger() {
|
||||
try {
|
||||
console.log('Testing manual processing trigger...');
|
||||
console.log("Testing manual processing trigger...");
|
||||
|
||||
const response = await fetch('http://localhost:3000/api/admin/trigger-processing', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Note: In a real scenario, you'd need to include authentication cookies
|
||||
// For testing, you might need to login first and copy the session cookie
|
||||
},
|
||||
body: JSON.stringify({
|
||||
batchSize: 5, // Process max 5 sessions
|
||||
maxConcurrency: 3 // Use 3 concurrent workers
|
||||
})
|
||||
});
|
||||
const response = await fetch(
|
||||
"http://localhost:3000/api/admin/trigger-processing",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
// Note: In a real scenario, you'd need to include authentication cookies
|
||||
// For testing, you might need to login first and copy the session cookie
|
||||
},
|
||||
body: JSON.stringify({
|
||||
batchSize: 5, // Process max 5 sessions
|
||||
maxConcurrency: 3, // Use 3 concurrent workers
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('✅ Manual trigger successful:');
|
||||
console.log("✅ Manual trigger successful:");
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
console.log('❌ Manual trigger failed:');
|
||||
console.log("❌ Manual trigger failed:");
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error testing manual trigger:', error.message);
|
||||
console.error("❌ Error testing manual trigger:", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,16 +10,18 @@ import { dirname, join } from "path";
|
||||
// Load environment variables from .env.local
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const envPath = join(__dirname, '..', '.env.local');
|
||||
const envPath = join(__dirname, "..", ".env.local");
|
||||
|
||||
try {
|
||||
const envFile = readFileSync(envPath, 'utf8');
|
||||
const envVars = envFile.split('\n').filter(line => line.trim() && !line.startsWith('#'));
|
||||
const envFile = readFileSync(envPath, "utf8");
|
||||
const envVars = envFile
|
||||
.split("\n")
|
||||
.filter((line) => line.trim() && !line.startsWith("#"));
|
||||
|
||||
envVars.forEach(line => {
|
||||
const [key, ...valueParts] = line.split('=');
|
||||
envVars.forEach((line) => {
|
||||
const [key, ...valueParts] = line.split("=");
|
||||
if (key && valueParts.length > 0) {
|
||||
const value = valueParts.join('=').trim();
|
||||
const value = valueParts.join("=").trim();
|
||||
if (!process.env[key.trim()]) {
|
||||
process.env[key.trim()] = value;
|
||||
}
|
||||
@ -65,11 +67,8 @@ async function triggerProcessingScheduler() {
|
||||
AND: [
|
||||
{ messages: { some: {} } },
|
||||
{
|
||||
OR: [
|
||||
{ processed: false },
|
||||
{ processed: null }
|
||||
]
|
||||
}
|
||||
OR: [{ processed: false }, { processed: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
@ -129,10 +128,7 @@ async function showProcessingStatus() {
|
||||
});
|
||||
const unprocessedSessions = await prisma.session.count({
|
||||
where: {
|
||||
OR: [
|
||||
{ processed: false },
|
||||
{ processed: null }
|
||||
]
|
||||
OR: [{ processed: false }, { processed: null }],
|
||||
},
|
||||
});
|
||||
const withMessages = await prisma.session.count({
|
||||
@ -147,11 +143,8 @@ async function showProcessingStatus() {
|
||||
AND: [
|
||||
{ messages: { some: {} } },
|
||||
{
|
||||
OR: [
|
||||
{ processed: false },
|
||||
{ processed: null }
|
||||
]
|
||||
}
|
||||
OR: [{ processed: false }, { processed: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
@ -170,11 +163,8 @@ async function showProcessingStatus() {
|
||||
AND: [
|
||||
{ messages: { some: {} } },
|
||||
{
|
||||
OR: [
|
||||
{ processed: false },
|
||||
{ processed: null }
|
||||
]
|
||||
}
|
||||
OR: [{ processed: false }, { processed: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
|
||||
@ -1,283 +0,0 @@
|
||||
// Script to manually process unprocessed sessions with OpenAI
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
||||
const OPENAI_API_URL = "https://api.openai.com/v1/chat/completions";
|
||||
|
||||
/**
|
||||
* Processes a session transcript using OpenAI API
|
||||
* @param {string} sessionId The session ID
|
||||
* @param {string} transcript The transcript content to process
|
||||
* @returns {Promise<Object>} Processed data from OpenAI
|
||||
*/
|
||||
async function processTranscriptWithOpenAI(sessionId, transcript) {
|
||||
if (!OPENAI_API_KEY) {
|
||||
throw new Error("OPENAI_API_KEY environment variable is not set");
|
||||
}
|
||||
|
||||
// Create a system message with instructions
|
||||
const systemMessage = `
|
||||
You are an AI assistant tasked with analyzing chat transcripts.
|
||||
Extract the following information from the transcript:
|
||||
1. The primary language used by the user (ISO 639-1 code)
|
||||
2. Number of messages sent by the user
|
||||
3. Overall sentiment (positive, neutral, or negative)
|
||||
4. Whether the conversation was escalated
|
||||
5. Whether HR contact was mentioned or provided
|
||||
6. The best-fitting category for the conversation from this list:
|
||||
- Schedule & Hours
|
||||
- Leave & Vacation
|
||||
- Sick Leave & Recovery
|
||||
- Salary & Compensation
|
||||
- Contract & Hours
|
||||
- Onboarding
|
||||
- Offboarding
|
||||
- Workwear & Staff Pass
|
||||
- Team & Contacts
|
||||
- Personal Questions
|
||||
- Access & Login
|
||||
- Social questions
|
||||
- Unrecognized / Other
|
||||
7. Up to 5 paraphrased questions asked by the user (in English)
|
||||
8. A brief summary of the conversation (10-300 characters)
|
||||
|
||||
Return the data in JSON format matching this schema:
|
||||
{
|
||||
"language": "ISO 639-1 code",
|
||||
"messages_sent": number,
|
||||
"sentiment": "positive|neutral|negative",
|
||||
"escalated": boolean,
|
||||
"forwarded_hr": boolean,
|
||||
"category": "one of the categories listed above",
|
||||
"questions": ["question 1", "question 2", ...],
|
||||
"summary": "brief summary",
|
||||
"session_id": "${sessionId}"
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await fetch(OPENAI_API_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${OPENAI_API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "gpt-4-turbo",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: systemMessage,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: transcript,
|
||||
},
|
||||
],
|
||||
temperature: 0.3, // Lower temperature for more consistent results
|
||||
response_format: { type: "json_object" },
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`OpenAI API error: ${response.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const processedData = JSON.parse(data.choices[0].message.content);
|
||||
|
||||
// Validate the response against our expected schema
|
||||
validateOpenAIResponse(processedData);
|
||||
|
||||
return processedData;
|
||||
} catch (error) {
|
||||
console.error(`Error processing transcript with OpenAI:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the OpenAI response against our expected schema
|
||||
* @param {Object} data The data to validate
|
||||
*/
|
||||
function validateOpenAIResponse(data) {
|
||||
// Check required fields
|
||||
const requiredFields = [
|
||||
"language",
|
||||
"messages_sent",
|
||||
"sentiment",
|
||||
"escalated",
|
||||
"forwarded_hr",
|
||||
"category",
|
||||
"questions",
|
||||
"summary",
|
||||
"session_id",
|
||||
];
|
||||
|
||||
for (const field of requiredFields) {
|
||||
if (!(field in data)) {
|
||||
throw new Error(`Missing required field: ${field}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate field types
|
||||
if (typeof data.language !== "string" || !/^[a-z]{2}$/.test(data.language)) {
|
||||
throw new Error(
|
||||
"Invalid language format. Expected ISO 639-1 code (e.g., 'en')"
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof data.messages_sent !== "number" || data.messages_sent < 0) {
|
||||
throw new Error("Invalid messages_sent. Expected non-negative number");
|
||||
}
|
||||
|
||||
if (!["positive", "neutral", "negative"].includes(data.sentiment)) {
|
||||
throw new Error(
|
||||
"Invalid sentiment. Expected 'positive', 'neutral', or 'negative'"
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof data.escalated !== "boolean") {
|
||||
throw new Error("Invalid escalated. Expected boolean");
|
||||
}
|
||||
|
||||
if (typeof data.forwarded_hr !== "boolean") {
|
||||
throw new Error("Invalid forwarded_hr. Expected boolean");
|
||||
}
|
||||
|
||||
const validCategories = [
|
||||
"Schedule & Hours",
|
||||
"Leave & Vacation",
|
||||
"Sick Leave & Recovery",
|
||||
"Salary & Compensation",
|
||||
"Contract & Hours",
|
||||
"Onboarding",
|
||||
"Offboarding",
|
||||
"Workwear & Staff Pass",
|
||||
"Team & Contacts",
|
||||
"Personal Questions",
|
||||
"Access & Login",
|
||||
"Social questions",
|
||||
"Unrecognized / Other",
|
||||
];
|
||||
|
||||
if (!validCategories.includes(data.category)) {
|
||||
throw new Error(
|
||||
`Invalid category. Expected one of: ${validCategories.join(", ")}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.questions)) {
|
||||
throw new Error("Invalid questions. Expected array of strings");
|
||||
}
|
||||
|
||||
if (
|
||||
typeof data.summary !== "string" ||
|
||||
data.summary.length < 10 ||
|
||||
data.summary.length > 300
|
||||
) {
|
||||
throw new Error(
|
||||
"Invalid summary. Expected string between 10-300 characters"
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof data.session_id !== "string") {
|
||||
throw new Error("Invalid session_id. Expected string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to process unprocessed sessions
|
||||
*/
|
||||
async function processUnprocessedSessions() {
|
||||
console.log("Starting to process unprocessed sessions...");
|
||||
|
||||
// Find sessions that have transcript content but haven't been processed
|
||||
const sessionsToProcess = await prisma.session.findMany({
|
||||
where: {
|
||||
AND: [
|
||||
{ transcriptContent: { not: null } },
|
||||
{ transcriptContent: { not: "" } },
|
||||
{ processed: { not: true } }, // Either false or null
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
transcriptContent: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (sessionsToProcess.length === 0) {
|
||||
console.log("No sessions found requiring processing.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${sessionsToProcess.length} sessions to process.`);
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const session of sessionsToProcess) {
|
||||
if (!session.transcriptContent) {
|
||||
// Should not happen due to query, but good for type safety
|
||||
console.warn(
|
||||
`Session ${session.id} has no transcript content, skipping.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Processing transcript for session ${session.id}...`);
|
||||
try {
|
||||
const processedData = await processTranscriptWithOpenAI(
|
||||
session.id,
|
||||
session.transcriptContent
|
||||
);
|
||||
|
||||
// Map sentiment string to float value for compatibility with existing data
|
||||
const sentimentMap = {
|
||||
positive: 0.8,
|
||||
neutral: 0.0,
|
||||
negative: -0.8,
|
||||
};
|
||||
|
||||
// Update the session with processed data
|
||||
await prisma.session.update({
|
||||
where: { id: session.id },
|
||||
data: {
|
||||
language: processedData.language,
|
||||
messagesSent: processedData.messages_sent,
|
||||
sentiment: sentimentMap[processedData.sentiment] || 0,
|
||||
sentimentCategory: processedData.sentiment,
|
||||
escalated: processedData.escalated,
|
||||
forwardedHr: processedData.forwarded_hr,
|
||||
category: processedData.category,
|
||||
questions: JSON.stringify(processedData.questions),
|
||||
summary: processedData.summary,
|
||||
processed: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Successfully processed session ${session.id}.`);
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
console.error(`Error processing session ${session.id}:`, error);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Session processing complete.");
|
||||
console.log(`Successfully processed: ${successCount} sessions.`);
|
||||
console.log(`Failed to process: ${errorCount} sessions.`);
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
processUnprocessedSessions()
|
||||
.catch((e) => {
|
||||
console.error("An error occurred during the script execution:", e);
|
||||
process.exitCode = 1;
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
48
scripts/reset-processed-status.js
Normal file
48
scripts/reset-processed-status.js
Normal file
@ -0,0 +1,48 @@
|
||||
// Reset all sessions to processed: false for reprocessing with new instructions
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function resetProcessedStatus() {
|
||||
try {
|
||||
console.log('🔄 Resetting processed status for all sessions...');
|
||||
|
||||
// Get count of currently processed sessions
|
||||
const processedCount = await prisma.session.count({
|
||||
where: { processed: true }
|
||||
});
|
||||
|
||||
console.log(`📊 Found ${processedCount} processed sessions to reset`);
|
||||
|
||||
if (processedCount === 0) {
|
||||
console.log('✅ No sessions need to be reset');
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset all sessions to processed: false
|
||||
const result = await prisma.session.updateMany({
|
||||
where: { processed: true },
|
||||
data: {
|
||||
processed: false,
|
||||
// Also reset AI-generated fields so they get fresh analysis
|
||||
sentimentCategory: null,
|
||||
category: null,
|
||||
questions: null,
|
||||
summary: null,
|
||||
validData: true // Reset to default
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ Successfully reset ${result.count} sessions to processed: false`);
|
||||
console.log('🤖 These sessions will be reprocessed with the new OpenAI instructions');
|
||||
console.log('🎯 Quality validation will now mark invalid data appropriately');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error resetting processed status:', error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
resetProcessedStatus();
|
||||
83
scripts/test-automation.js
Normal file
83
scripts/test-automation.js
Normal file
@ -0,0 +1,83 @@
|
||||
// Test script to demonstrate the automated processing system
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { processUnprocessedSessions, startProcessingScheduler } from '../lib/processingScheduler.ts';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function testAutomation() {
|
||||
console.log('🧪 TESTING AUTOMATED PROCESSING SYSTEM\n');
|
||||
|
||||
// Step 1: Show current status
|
||||
console.log('📊 STEP 1: Current Database Status');
|
||||
console.log('=' .repeat(50));
|
||||
await showStatus();
|
||||
|
||||
// Step 2: Test the automated function
|
||||
console.log('\n🤖 STEP 2: Testing Automated Processing Function');
|
||||
console.log('=' .repeat(50));
|
||||
console.log('This is the SAME function that runs automatically every hour...\n');
|
||||
|
||||
try {
|
||||
// This is the EXACT same function that runs automatically every hour
|
||||
const result = await processUnprocessedSessions(5, 2); // Smaller batch for demo
|
||||
|
||||
console.log('\n✅ AUTOMATION TEST RESULTS:');
|
||||
console.log(` 📊 Sessions processed: ${result.totalProcessed}`);
|
||||
console.log(` ❌ Sessions failed: ${result.totalFailed}`);
|
||||
console.log(` ⏱️ Processing time: ${result.totalTime.toFixed(2)}s`);
|
||||
|
||||
if (result.totalProcessed === 0 && result.totalFailed === 0) {
|
||||
console.log('\n🎉 PERFECT! No unprocessed sessions found.');
|
||||
console.log('✅ This means the automation is working - everything is already processed!');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error testing automation:', error);
|
||||
}
|
||||
|
||||
// Step 3: Show what the scheduler does
|
||||
console.log('\n⏰ STEP 3: Automated Scheduler Information');
|
||||
console.log('=' .repeat(50));
|
||||
console.log('🔄 HOURLY AUTOMATION:');
|
||||
console.log(' • Runs every hour: cron.schedule("0 * * * *")');
|
||||
console.log(' • Checks: WHERE processed = false AND messages: { some: {} }');
|
||||
console.log(' • Processes: ALL unprocessed sessions through OpenAI');
|
||||
console.log(' • Continues: Until NO unprocessed sessions remain');
|
||||
console.log(' • Quality: Validates and filters low-quality sessions');
|
||||
|
||||
console.log('\n🚀 DASHBOARD INTEGRATION:');
|
||||
console.log(' • Refresh button triggers: triggerCompleteWorkflow()');
|
||||
console.log(' • Fetches transcripts: For sessions without messages');
|
||||
console.log(' • Processes everything: Until all sessions are analyzed');
|
||||
|
||||
console.log('\n🎯 PRODUCTION STATUS:');
|
||||
console.log(' ✅ System is FULLY AUTOMATED');
|
||||
console.log(' ✅ No manual intervention needed');
|
||||
console.log(' ✅ Processes new data automatically');
|
||||
console.log(' ✅ Quality validation included');
|
||||
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
async function showStatus() {
|
||||
const totalSessions = await prisma.session.count();
|
||||
const processedSessions = await prisma.session.count({ where: { processed: true } });
|
||||
const unprocessedSessions = await prisma.session.count({ where: { processed: false } });
|
||||
const sessionsWithMessages = await prisma.session.count({
|
||||
where: { messages: { some: {} } }
|
||||
});
|
||||
|
||||
console.log(`📈 Total sessions: ${totalSessions}`);
|
||||
console.log(`✅ Processed sessions: ${processedSessions}`);
|
||||
console.log(`⏳ Unprocessed sessions: ${unprocessedSessions}`);
|
||||
console.log(`💬 Sessions with messages: ${sessionsWithMessages}`);
|
||||
|
||||
if (processedSessions === sessionsWithMessages && unprocessedSessions === 0) {
|
||||
console.log('\n🎉 AUTOMATION WORKING PERFECTLY!');
|
||||
console.log('✅ All sessions with messages have been processed');
|
||||
console.log('✅ No unprocessed sessions remaining');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testAutomation();
|
||||
47
scripts/test-improved-prompt.js
Normal file
47
scripts/test-improved-prompt.js
Normal file
@ -0,0 +1,47 @@
|
||||
// Test the improved prompt on a few sessions
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function testImprovedPrompt() {
|
||||
console.log('🧪 TESTING IMPROVED QUESTION EXTRACTION PROMPT\n');
|
||||
|
||||
// Reset a few sessions to test the new prompt
|
||||
console.log('📝 Resetting 5 sessions to test improved prompt...');
|
||||
|
||||
const sessionsToReprocess = await prisma.session.findMany({
|
||||
where: {
|
||||
processed: true,
|
||||
questions: '[]' // Sessions with empty questions
|
||||
},
|
||||
take: 5
|
||||
});
|
||||
|
||||
if (sessionsToReprocess.length > 0) {
|
||||
// Reset these sessions to unprocessed
|
||||
await prisma.session.updateMany({
|
||||
where: {
|
||||
id: { in: sessionsToReprocess.map(s => s.id) }
|
||||
},
|
||||
data: {
|
||||
processed: false,
|
||||
questions: null,
|
||||
summary: null
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ Reset ${sessionsToReprocess.length} sessions for reprocessing`);
|
||||
console.log('Session IDs:', sessionsToReprocess.map(s => s.id));
|
||||
|
||||
console.log('\n🚀 Now run this command to test the improved prompt:');
|
||||
console.log('npx tsx scripts/trigger-processing-direct.js');
|
||||
console.log('\nThen check the results with:');
|
||||
console.log('npx tsx scripts/check-questions-issue.js');
|
||||
} else {
|
||||
console.log('❌ No sessions with empty questions found to reprocess');
|
||||
}
|
||||
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
testImprovedPrompt();
|
||||
@ -1,36 +1,39 @@
|
||||
// Script to check processing status and trigger processing
|
||||
// Usage: node scripts/test-processing-status.js
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function checkProcessingStatus() {
|
||||
try {
|
||||
console.log('🔍 Checking processing status...\n');
|
||||
console.log("🔍 Checking processing status...\n");
|
||||
|
||||
// Get processing status
|
||||
const totalSessions = await prisma.session.count();
|
||||
const processedSessions = await prisma.session.count({
|
||||
where: { processed: true }
|
||||
where: { processed: true },
|
||||
});
|
||||
const unprocessedSessions = await prisma.session.count({
|
||||
where: { processed: false }
|
||||
where: { processed: false },
|
||||
});
|
||||
const sessionsWithMessages = await prisma.session.count({
|
||||
where: {
|
||||
processed: false,
|
||||
messages: { some: {} }
|
||||
}
|
||||
messages: { some: {} },
|
||||
},
|
||||
});
|
||||
|
||||
console.log('📊 Processing Status:');
|
||||
console.log("📊 Processing Status:");
|
||||
console.log(` Total sessions: ${totalSessions}`);
|
||||
console.log(` ✅ Processed: ${processedSessions}`);
|
||||
console.log(` ⏳ Unprocessed: ${unprocessedSessions}`);
|
||||
console.log(` 📝 Unprocessed with messages: ${sessionsWithMessages}`);
|
||||
|
||||
const processedPercentage = ((processedSessions / totalSessions) * 100).toFixed(1);
|
||||
const processedPercentage = (
|
||||
(processedSessions / totalSessions) *
|
||||
100
|
||||
).toFixed(1);
|
||||
console.log(` 📈 Processing progress: ${processedPercentage}%\n`);
|
||||
|
||||
// Check recent processing activity
|
||||
@ -38,35 +41,40 @@ async function checkProcessingStatus() {
|
||||
where: {
|
||||
processed: true,
|
||||
createdAt: {
|
||||
gte: new Date(Date.now() - 60 * 60 * 1000) // Last hour
|
||||
}
|
||||
gte: new Date(Date.now() - 60 * 60 * 1000), // Last hour
|
||||
},
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
orderBy: { createdAt: "desc" },
|
||||
take: 5,
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
category: true,
|
||||
sentiment: true
|
||||
}
|
||||
sentiment: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (recentlyProcessed.length > 0) {
|
||||
console.log('🕒 Recently processed sessions:');
|
||||
recentlyProcessed.forEach(session => {
|
||||
const timeAgo = Math.round((Date.now() - session.createdAt.getTime()) / 1000 / 60);
|
||||
console.log(` • ${session.id.substring(0, 8)}... (${timeAgo}m ago) - ${session.category || 'No category'}`);
|
||||
console.log("🕒 Recently processed sessions:");
|
||||
recentlyProcessed.forEach((session) => {
|
||||
const timeAgo = Math.round(
|
||||
(Date.now() - session.createdAt.getTime()) / 1000 / 60
|
||||
);
|
||||
console.log(
|
||||
` • ${session.id.substring(0, 8)}... (${timeAgo}m ago) - ${session.category || "No category"}`
|
||||
);
|
||||
});
|
||||
} else {
|
||||
console.log('🕒 No sessions processed in the last hour');
|
||||
console.log("🕒 No sessions processed in the last hour");
|
||||
}
|
||||
|
||||
console.log('\n✨ Processing system is working correctly!');
|
||||
console.log('💡 The parallel processing successfully processed sessions.');
|
||||
console.log('🎯 For manual triggers, you need to be logged in as an admin user.');
|
||||
|
||||
console.log("\n✨ Processing system is working correctly!");
|
||||
console.log("💡 The parallel processing successfully processed sessions.");
|
||||
console.log(
|
||||
"🎯 For manual triggers, you need to be logged in as an admin user."
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('❌ Error checking status:', error);
|
||||
console.error("❌ Error checking status:", error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
57
scripts/trigger-csv-refresh.js
Normal file
57
scripts/trigger-csv-refresh.js
Normal file
@ -0,0 +1,57 @@
|
||||
// Trigger CSV refresh for all companies
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function triggerCsvRefresh() {
|
||||
try {
|
||||
console.log('🔄 Triggering CSV refresh for all companies...\n');
|
||||
|
||||
// Get all companies
|
||||
const companies = await prisma.company.findMany();
|
||||
|
||||
if (companies.length === 0) {
|
||||
console.log('❌ No companies found. Run seed script first.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🏢 Found ${companies.length} companies:`);
|
||||
|
||||
for (const company of companies) {
|
||||
console.log(`📊 Company: ${company.name} (ID: ${company.id})`);
|
||||
console.log(`📥 CSV URL: ${company.csvUrl}`);
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/admin/refresh-sessions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
companyId: company.id
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log(`✅ Successfully imported ${result.imported} sessions for ${company.name}`);
|
||||
} else {
|
||||
console.log(`❌ Error for ${company.name}: ${result.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ Failed to refresh ${company.name}: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log(''); // Empty line for readability
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error triggering CSV refresh:', error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
triggerCsvRefresh();
|
||||
@ -1,20 +1,21 @@
|
||||
// Direct trigger for processing scheduler (bypasses authentication)
|
||||
// Usage: node scripts/trigger-processing-direct.js
|
||||
|
||||
import { processUnprocessedSessions } from '../lib/processingScheduler.js';
|
||||
// Direct processing trigger without authentication
|
||||
import { processUnprocessedSessions } from '../lib/processingScheduler.ts';
|
||||
|
||||
async function triggerProcessing() {
|
||||
try {
|
||||
console.log('🚀 Manually triggering processing scheduler...\n');
|
||||
console.log('🤖 Starting complete batch processing of all unprocessed sessions...\n');
|
||||
|
||||
// Process with custom parameters
|
||||
await processUnprocessedSessions(50, 3); // Process 50 sessions with 3 concurrent workers
|
||||
// Process all unprocessed sessions in batches until completion
|
||||
const result = await processUnprocessedSessions(10, 3);
|
||||
|
||||
console.log('\n✅ Processing trigger completed!');
|
||||
console.log('\n🎉 Complete processing finished!');
|
||||
console.log(`📊 Final results: ${result.totalProcessed} processed, ${result.totalFailed} failed`);
|
||||
console.log(`⏱️ Total time: ${result.totalTime.toFixed(2)}s`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error triggering processing:', error);
|
||||
console.error('❌ Error during processing:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
triggerProcessing();
|
||||
|
||||
Reference in New Issue
Block a user