Skip to content

Commit 971b842

Browse files
committed
feat: add demo assessment seed script for 30-day history
1 parent 8d0b34c commit 971b842

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

scripts/seed-demo-assessments.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* Seed 30 days of assessment history for demo purposes.
3+
*
4+
* Usage: npx tsx scripts/seed-demo-assessments.ts <elderlyProfileId>
5+
*/
6+
7+
import { PrismaClient } from "@prisma/client";
8+
9+
const prisma = new PrismaClient();
10+
11+
function randomBetween(min: number, max: number) {
12+
return Math.round((Math.random() * (max - min) + min) * 100) / 100;
13+
}
14+
15+
function pickSeverity(score: number): "GREEN" | "YELLOW" | "RED" {
16+
if (score >= 0.7) return "GREEN";
17+
if (score >= 0.4) return "YELLOW";
18+
return "RED";
19+
}
20+
21+
const SUMMARIES = [
22+
"Good cognitive performance today. Memory recall was strong across all categories.",
23+
"Slightly below average performance. Struggled with orientation questions.",
24+
"Excellent session. Quick and confident responses throughout.",
25+
"Mixed results. Personal memory strong but general knowledge weaker.",
26+
"Below average today. May indicate fatigue or distraction.",
27+
"Very strong performance. Improvement noted compared to recent sessions.",
28+
"Average session. Consistent with recent trend.",
29+
"Some difficulty with people recognition questions today.",
30+
"Strong start but performance declined toward the end.",
31+
"Notable improvement in orientation questions compared to last week.",
32+
];
33+
34+
const MOODS = ["Happy", "Content", "Calm", "Neutral", "Tired", "Slightly anxious", "Good spirits", "Relaxed"];
35+
36+
async function main() {
37+
const profileId = process.argv[2];
38+
if (!profileId) {
39+
console.error("Usage: npx tsx scripts/seed-demo-assessments.ts <elderlyProfileId>");
40+
process.exit(1);
41+
}
42+
43+
// Verify profile exists
44+
const profile = await prisma.elderlyProfile.findUnique({ where: { id: profileId } });
45+
if (!profile) {
46+
console.error(`Profile ${profileId} not found`);
47+
process.exit(1);
48+
}
49+
50+
// Get or create config
51+
let config = await prisma.assessmentConfig.findUnique({
52+
where: { elderlyProfileId: profileId },
53+
});
54+
if (!config) {
55+
config = await prisma.assessmentConfig.create({
56+
data: {
57+
elderlyProfileId: profileId,
58+
scheduledTime: "09:00",
59+
questionsPerCall: 4,
60+
active: true,
61+
},
62+
});
63+
}
64+
65+
// Get questions
66+
const questions = await prisma.assessmentQuestion.findMany({
67+
where: { elderlyProfileId: profileId },
68+
});
69+
if (questions.length < 4) {
70+
console.error(`Profile needs at least 4 questions, has ${questions.length}. Set up questions first.`);
71+
process.exit(1);
72+
}
73+
74+
console.log(`Seeding 30 days of assessments for "${profile.name}" (${profileId})...`);
75+
76+
// Create a slight upward trend with some variance
77+
const baseScore = 0.55; // start around 55%
78+
const dailyImprovement = 0.008; // ~0.8% improvement per day
79+
80+
for (let daysAgo = 30; daysAgo >= 1; daysAgo--) {
81+
const date = new Date();
82+
date.setDate(date.getDate() - daysAgo);
83+
date.setHours(9, 0, 0, 0);
84+
const dateStr = date.toISOString().split("T")[0];
85+
86+
// Skip ~20% of days randomly (weekends, missed calls)
87+
if (Math.random() < 0.15 && daysAgo > 2) continue;
88+
89+
const dayIndex = 30 - daysAgo;
90+
const trendScore = Math.min(0.95, baseScore + dailyImprovement * dayIndex);
91+
const noise = randomBetween(-0.12, 0.12);
92+
const overallScore = Math.max(0.15, Math.min(1.0, trendScore + noise));
93+
const severity = pickSeverity(overallScore);
94+
95+
// Vocal analysis with correlated values
96+
const wellnessScore = Math.round(Math.max(10, Math.min(100, overallScore * 100 + randomBetween(-15, 15))));
97+
const moodScore = Math.round(Math.max(10, Math.min(100, wellnessScore + randomBetween(-10, 10))));
98+
const depressionIndex = Math.round(Math.max(5, Math.min(80, 100 - wellnessScore + randomBetween(-10, 15))));
99+
const parkinsonsRisk = Math.round(Math.max(3, Math.min(50, 20 + randomBetween(-10, 15))));
100+
const speechFluency = Math.round(Math.max(30, Math.min(100, overallScore * 100 + randomBetween(-8, 12))));
101+
102+
const mood = MOODS[Math.floor(Math.random() * MOODS.length)];
103+
104+
const vocalAnalysis = {
105+
parkinsons: {
106+
currentProbability: parkinsonsRisk,
107+
futureRisk: Math.round(parkinsonsRisk + randomBetween(2, 10)),
108+
details: parkinsonsRisk > 30
109+
? "Slight vocal tremor detected in sustained vowels."
110+
: "Voice patterns within normal range.",
111+
},
112+
depression: {
113+
currentState: depressionIndex,
114+
futurePropensity: Math.round(depressionIndex + randomBetween(-5, 10)),
115+
details: depressionIndex > 40
116+
? "Reduced vocal energy and slower speech rate noted."
117+
: "Vocal markers suggest stable emotional state.",
118+
},
119+
mood: {
120+
todayMood: mood,
121+
wellnessScore,
122+
details: `Overall ${mood.toLowerCase()} demeanor during the call.`,
123+
},
124+
// Flat keys for radar chart
125+
parkinsonsRisk,
126+
depressionIndex,
127+
wellnessScore,
128+
moodScore,
129+
speechFluency,
130+
};
131+
132+
const summary = SUMMARIES[Math.floor(Math.random() * SUMMARIES.length)];
133+
134+
// Pick 4 random questions for this session
135+
const sessionQuestions = [...questions].sort(() => Math.random() - 0.5).slice(0, 4);
136+
137+
const session = await prisma.assessmentSession.create({
138+
data: {
139+
elderlyProfileId: profileId,
140+
configId: config.id,
141+
date: dateStr,
142+
overallScore,
143+
status: "COMPLETED",
144+
summary,
145+
severity,
146+
vocalAnalysis,
147+
emotionalResponse: overallScore > 0.6 ? "Positive and engaged" : "Somewhat subdued",
148+
createdAt: date,
149+
answers: {
150+
create: sessionQuestions.map((q, i) => {
151+
const isCorrect = Math.random() < overallScore;
152+
return {
153+
questionId: q.id,
154+
questionText: q.questionText,
155+
correctAnswer: q.correctAnswer,
156+
elderAnswer: isCorrect ? q.correctAnswer : (Math.random() < 0.3 ? null : "I'm not sure"),
157+
result: isCorrect ? "CORRECT" : (Math.random() < 0.2 ? "UNCLEAR" : "WRONG"),
158+
orderIndex: i,
159+
createdAt: date,
160+
};
161+
}),
162+
},
163+
},
164+
});
165+
166+
console.log(` ${dateStr}: ${Math.round(overallScore * 100)}% (${severity}) - ${session.id}`);
167+
}
168+
169+
console.log("\nDone! Assessment history seeded.");
170+
await prisma.$disconnect();
171+
}
172+
173+
main().catch((e) => {
174+
console.error(e);
175+
prisma.$disconnect();
176+
process.exit(1);
177+
});

0 commit comments

Comments
 (0)