| 1 | import { prisma } from "@app/lib/prisma";
|
| 2 | import { InsightType, InsightPeriod, BaseMoodOption } from "@prisma/client";
|
| 3 | import { InsightPeriodPayload } from "@app/lib/queue/types";
|
| 4 |
|
| 5 | const MOOD_SCORE: Record<BaseMoodOption, number> = {
|
| 6 | GREAT: 5,
|
| 7 | GOOD: 4,
|
| 8 | NEUTRAL: 3,
|
| 9 | SAD: 2,
|
| 10 | ANGRY: 1,
|
| 11 | };
|
| 12 |
|
| 13 | const avg = (values: number[]) =>
|
| 14 | values.reduce((a, b) => a + b, 0) / values.length;
|
| 15 |
|
| 16 | export async function processMoodTrend({
|
| 17 | userId,
|
| 18 | periodStart,
|
| 19 | periodEnd,
|
| 20 | }: InsightPeriodPayload): Promise<void> {
|
| 21 | const start = new Date(periodStart);
|
| 22 | const end = new Date(periodEnd);
|
| 23 |
|
| 24 | const moods = await prisma.mood.findMany({
|
| 25 | where: { userId, moment: { gte: start, lte: end } },
|
| 26 | orderBy: { moment: "asc" },
|
| 27 | select: { selectedMood: true, energyLevel: true, moment: true },
|
| 28 | });
|
| 29 |
|
| 30 | if (moods.length < 3) return;
|
| 31 |
|
| 32 | const scores = moods.map((m) => MOOD_SCORE[m.selectedMood]);
|
| 33 | const mid = Math.floor(scores.length / 2);
|
| 34 | const avgFirst = avg(scores.slice(0, mid));
|
| 35 | const avgSecond = avg(scores.slice(mid));
|
| 36 | const delta = Math.round(((avgSecond - avgFirst) / avgFirst) * 100);
|
| 37 |
|
| 38 | // Dominant mood in the period
|
| 39 | const moodCounts = moods.reduce<Partial<Record<BaseMoodOption, number>>>(
|
| 40 | (acc, m) => ({ ...acc, [m.selectedMood]: (acc[m.selectedMood] ?? 0) + 1 }),
|
| 41 | {},
|
| 42 | );
|
| 43 | const dominantMood = (
|
| 44 | Object.entries(moodCounts) as [BaseMoodOption, number][]
|
| 45 | ).sort((a, b) => b[1] - a[1])[0][0];
|
| 46 |
|
| 47 | const avgEnergy = avg(moods.map((m) => m.energyLevel));
|
| 48 |
|
| 49 | await prisma.insight.upsert({
|
| 50 | where: {
|
| 51 | userId_type_periodStart: {
|
| 52 | userId,
|
| 53 | type: InsightType.MOOD_TREND,
|
| 54 | periodStart: start,
|
| 55 | },
|
| 56 | },
|
| 57 | update: {
|
| 58 | body: buildBody(delta),
|
| 59 | metadata: { delta, avgFirst, avgSecond, dominantMood, avgEnergy },
|
| 60 | generatedAt: new Date(),
|
| 61 | },
|
| 62 | create: {
|
| 63 | userId,
|
| 64 | type: InsightType.MOOD_TREND,
|
| 65 | period: InsightPeriod.WEEKLY,
|
| 66 | title: "Tendência de Humor",
|
| 67 | body: buildBody(delta),
|
| 68 | metadata: { delta, avgFirst, avgSecond, dominantMood, avgEnergy },
|
| 69 | periodStart: start,
|
| 70 | periodEnd: end,
|
| 71 | },
|
| 72 | });
|
| 73 | }
|
| 74 |
|
| 75 | function buildBody(delta: number): string {
|
| 76 | if (delta > 10)
|
| 77 | return `Seu humor melhorou ${delta}% em relação ao início do período.`;
|
| 78 | if (delta < -10)
|
| 79 | return `Seu humor caiu ${Math.abs(delta)}% em relação ao início do período.`;
|
| 80 | return "Seu humor se manteve estável durante o período.";
|
| 81 | }
|