Skip to content

Commit 39ca006

Browse files
fix dbdbdbdbdb
1 parent 2e0fbd5 commit 39ca006

6 files changed

Lines changed: 155 additions & 16 deletions

File tree

backend/database/career_advisor.db

-252 KB
Binary file not shown.

backend/database/career_catalog.db

0 Bytes
Binary file not shown.

backend/scripts/checkDbCounts.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const path = require('path');
2+
const sqlite3 = require('sqlite3').verbose();
3+
4+
const catalogDbPath = path.join(__dirname, '../database/career_catalog.db');
5+
const advisorDbPath = path.join(__dirname, '../database/career_advisor.db');
6+
7+
console.log('📊 Checking database counts...\n');
8+
9+
// Check career_catalog.db
10+
const catalogDb = new sqlite3.Database(catalogDbPath, (err) => {
11+
if (err) {
12+
console.error('❌ Cannot open career_catalog.db:', err.message);
13+
return;
14+
}
15+
16+
catalogDb.get('SELECT COUNT(*) as count FROM jobs', (err, row) => {
17+
if (err) {
18+
console.error('❌ Error querying catalog:', err.message);
19+
} else {
20+
console.log(`✅ career_catalog.db (jobs table): ${row.count} records`);
21+
}
22+
catalogDb.close();
23+
});
24+
});
25+
26+
// Check career_advisor.db
27+
const advisorDb = new sqlite3.Database(advisorDbPath, (err) => {
28+
if (err) {
29+
console.error('❌ Cannot open career_advisor.db:', err.message);
30+
return;
31+
}
32+
33+
advisorDb.get('SELECT COUNT(*) as count FROM careers', (err, row) => {
34+
if (err) {
35+
console.error('❌ Error querying advisor:', err.message);
36+
} else {
37+
console.log(`✅ career_advisor.db (careers table): ${row.count} records`);
38+
}
39+
40+
// Check duplicates
41+
advisorDb.all(`
42+
SELECT name, COUNT(*) as count
43+
FROM careers
44+
GROUP BY name
45+
HAVING count > 1
46+
`, (err, rows) => {
47+
if (err) {
48+
console.error('❌ Error checking duplicates:', err.message);
49+
} else if (rows.length > 0) {
50+
console.log(`\n⚠️ Found ${rows.length} duplicate careers`);
51+
} else {
52+
console.log('\n✅ No duplicates found');
53+
}
54+
advisorDb.close();
55+
});
56+
});
57+
});

backend/scripts/cleanAdvisorDb.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const path = require('path');
2+
const sqlite3 = require('sqlite3').verbose();
3+
4+
const advisorDbPath = path.join(__dirname, '../database/career_advisor.db');
5+
const { buildCareerRecords } = require('../data/careerLibrary');
6+
7+
console.log('🧹 Cleaning up career_advisor.db to match careerLibrary...\n');
8+
9+
const db = new sqlite3.Database(advisorDbPath, (err) => {
10+
if (err) {
11+
console.error('❌ Cannot open database:', err.message);
12+
process.exit(1);
13+
}
14+
});
15+
16+
const careers = buildCareerRecords();
17+
console.log(`📊 Career library has ${careers.length} careers\n`);
18+
19+
// Clear existing careers
20+
db.run('DELETE FROM careers', (err) => {
21+
if (err) {
22+
console.error('❌ Error clearing careers:', err.message);
23+
db.close();
24+
process.exit(1);
25+
}
26+
27+
console.log('✅ Cleared existing careers');
28+
29+
// Insert new careers
30+
const stmt = db.prepare(`
31+
INSERT INTO careers (name, category, required_skills, salary_range, job_outlook, description)
32+
VALUES (?, ?, ?, ?, ?, ?)
33+
`);
34+
35+
let inserted = 0;
36+
for (const career of careers) {
37+
stmt.run(
38+
career.name,
39+
career.category,
40+
JSON.stringify(career.required_skills || []),
41+
career.salary_range || 'Negotiable',
42+
career.job_outlook || 'good',
43+
career.description || ''
44+
);
45+
inserted++;
46+
}
47+
48+
stmt.finalize(() => {
49+
db.get('SELECT COUNT(*) as count FROM careers', (err, row) => {
50+
if (err) {
51+
console.error('❌ Error counting:', err.message);
52+
} else {
53+
console.log(`✅ Inserted ${inserted} careers`);
54+
console.log(`✅ Final count: ${row.count} careers`);
55+
}
56+
db.close();
57+
});
58+
});
59+
});

backend/services/questionEngine.js

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,31 @@ function getConversationState(conversationId, userType = 'high_school') {
2828
function getNextQuestion(conversationId, userType) {
2929
const state = getConversationState(conversationId, userType);
3030

31-
// Initial mode: ask 5-7 broad questions
32-
if (state.answers.length >= 7) {
31+
// Initial mode: ask 20-30 broad questions
32+
if (state.answers.length >= 25) {
3333
return null; // Done with initial questions
3434
}
3535

3636
// Get broad questions from different categories
37-
const categories = ['skill', 'interest', 'scenario'];
37+
const categories = ['skill', 'interest', 'scenario', 'workstyle'];
3838
const targetCategory = categories[state.answers.length % categories.length];
3939

4040
const availableQuestions = ALL_QUESTIONS.filter(q =>
4141
q.category === targetCategory &&
42-
!state.asked Questions.has(q.id)
42+
!state.askedQuestions.has(q.id)
4343
);
4444

4545
if (availableQuestions.length === 0) {
46-
return null;
46+
// Fallback to any category if no questions in target category
47+
const anyAvailable = ALL_QUESTIONS.filter(q => !state.askedQuestions.has(q.id));
48+
if (anyAvailable.length === 0) return null;
49+
const question = anyAvailable[0];
50+
return {
51+
id: question.id,
52+
text: question.text,
53+
type: 'yes_no_maybe',
54+
options: ['Có', 'Có thể', 'Không']
55+
};
4756
}
4857

4958
// Return first available question
@@ -200,33 +209,47 @@ function getCareerRecommendations(conversationId) {
200209
// Combine initial and refinement answers
201210
const allAnswers = [...state.answers, ...state.refinementAnswers];
202211

203-
if (allAnswers.length < 3) {
204-
throw new Error('Need at least 3 answers to generate recommendations');
212+
if (allAnswers.length < 10) {
213+
throw new Error(`Need at least 10 answers to generate recommendations (current: ${allAnswers.length})`);
205214
}
206215

207216
// Calculate scores
208217
const scores = calculateCareerScoresFromAnswers(allAnswers);
209218

210-
// Rank and normalize
211-
const sortedCareers = Object.entries(scores)
219+
// Filter out careers with very low scores (minimum threshold)
220+
const minScoreThreshold = Math.max(...Object.values(scores)) * 0.1; // 10% of max score
221+
const filteredScores = Object.entries(scores)
222+
.filter(([, score]) => score >= minScoreThreshold)
212223
.sort((a, b) => b[1] - a[1])
213224
.slice(0, 10);
214225

215-
const totalScore = sortedCareers.reduce((sum, [, score]) => sum + score, 0);
226+
if (filteredScores.length === 0) {
227+
throw new Error('No careers match your profile');
228+
}
229+
230+
// Softmax normalization for smoother probability distribution
231+
const maxScore = filteredScores[0][1];
232+
const expScores = filteredScores.map(([career, score]) => [
233+
career,
234+
Math.exp((score - maxScore) / 50) // Temperature parameter = 50
235+
]);
236+
237+
const sumExp = expScores.reduce((sum, [, exp]) => sum + exp, 0);
216238

217-
const recommendations = sortedCareers.map(([career, score]) => ({
239+
const recommendations = expScores.map(([career, exp], index) => ({
218240
career_name: career,
219-
match_score: score,
220-
probability: totalScore > 0 ? (score / totalScore) : 0.1,
221-
confidence: score > 100 ? 'high' : score > 50 ? 'medium' : 'low',
241+
match_score: Math.round(filteredScores[index][1]),
242+
probability: exp / sumExp,
243+
confidence: filteredScores[index][1] > 150 ? 'high' :
244+
filteredScores[index][1] > 75 ? 'medium' : 'low',
222245
reasons: generateReasons(career, allAnswers)
223246
}));
224247

225248
return {
226249
recommendations,
227250
totalAnswers: allAnswers.length,
228251
mode: state.mode,
229-
canRefine: state.mode === 'initial' && allAnswers.length >= 5
252+
canRefine: state.mode === 'initial' && allAnswers.length >= 20
230253
};
231254
}
232255

frontend/src/pages/Explore.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default function Explore() {
1515
const [page, setPage] = useState(0);
1616
const [error, setError] = useState('');
1717

18-
const limit = 24;
18+
const limit = 100; // Load 100 jobs by default
1919

2020
useEffect(() => {
2121
let cancelled = false;

0 commit comments

Comments
 (0)