eletrotupi / tcc/ api/src/lib/queue/processors/insights/moodTrend.ts7ff50aa722f838fa59d0afb52d1c2e2b96bf3e22
2.6 KB | 95 lines Raw
1
import { prisma } from "@app/lib/prisma";
2
import { InsightType, InsightPeriod, BaseMoodOption } from "@prisma/client";
3
import { InsightPeriodPayload } from "@app/lib/queue/types";
4
import { avg } from "@app/utils/calc";
5

6
const MOOD_SCORE: Record<BaseMoodOption, number> = {
7
  GREAT: 5,
8
  GOOD: 4,
9
  NEUTRAL: 3,
10
  SAD: 2,
11
  ANGRY: 1,
12
};
13

14
type MoodTrendEntry = { selectedMood: BaseMoodOption; energyLevel: number };
15

16
export type MoodTrend = {
17
  delta: number;
18
  avgFirst: number;
19
  avgSecond: number;
20
  dominantMood: BaseMoodOption;
21
  avgEnergy: number;
22
};
23

24
export function computeMoodTrend(moods: MoodTrendEntry[]): MoodTrend {
25
  const scores = moods.map((m) => MOOD_SCORE[m.selectedMood]);
26
  const mid = Math.floor(scores.length / 2);
27
  const avgFirst = avg(scores.slice(0, mid));
28
  const avgSecond = avg(scores.slice(mid));
29
  const delta = Math.round(((avgSecond - avgFirst) / avgFirst) * 100);
30

31
  // Dominant mood in the period
32
  const moodCounts = moods.reduce<Partial<Record<BaseMoodOption, number>>>(
33
    (acc, m) => ({ ...acc, [m.selectedMood]: (acc[m.selectedMood] ?? 0) + 1 }),
34
    {},
35
  );
36
  const dominantMood = (
37
    Object.entries(moodCounts) as [BaseMoodOption, number][]
38
  ).sort((a, b) => b[1] - a[1])[0][0];
39

40
  const avgEnergy = avg(moods.map((m) => m.energyLevel));
41

42
  return { delta, avgFirst, avgSecond, dominantMood, avgEnergy };
43
}
44

45
export function moodTrendBody(delta: number): string {
46
  if (delta > 10)
47
    return `Seu humor melhorou ${delta}% em relação ao início do período.`;
48
  if (delta < -10)
49
    return `Seu humor caiu ${Math.abs(delta)}% em relação ao início do período.`;
50
  return "Seu humor se manteve estável durante o período.";
51
}
52

53
export async function processMoodTrend({
54
  userId,
55
  periodStart,
56
  periodEnd,
57
}: InsightPeriodPayload): Promise<void> {
58
  const start = new Date(periodStart);
59
  const end = new Date(periodEnd);
60

61
  const moods = await prisma.mood.findMany({
62
    where: { userId, moment: { gte: start, lte: end } },
63
    orderBy: { moment: "asc" },
64
    select: { selectedMood: true, energyLevel: true, moment: true },
65
  });
66

67
  if (moods.length < 3) return;
68

69
  const trend = computeMoodTrend(moods);
70

71
  await prisma.insight.upsert({
72
    where: {
73
      userId_type_periodStart: {
74
        userId,
75
        type: InsightType.MOOD_TREND,
76
        periodStart: start,
77
      },
78
    },
79
    update: {
80
      body: moodTrendBody(trend.delta),
81
      metadata: trend,
82
      generatedAt: new Date(),
83
    },
84
    create: {
85
      userId,
86
      type: InsightType.MOOD_TREND,
87
      period: InsightPeriod.WEEKLY,
88
      title: "Tendência de Humor",
89
      body: moodTrendBody(trend.delta),
90
      metadata: trend,
91
      periodStart: start,
92
      periodEnd: end,
93
    },
94
  });
95
}