eletrotupi / tcc/ api/src/lib/queue/processors/insights/dailyEnergy.tsa6fd2536de855fa5edadf135809eb4944828c6ab
2.9 KB | 101 lines Raw
1
import { prisma } from "@app/lib/prisma";
2
import { InsightType, InsightPeriod } from "@prisma/client";
3
import { InsightPeriodPayload } from "@app/lib/queue/types";
4
import { avg } from "@app/utils/calc";
5
import { appDayWindow } from "@app/lib/queue/processors/insights/utils";
6

7
export async function processDailyEnergy({
8
  userId,
9
  periodStart,
10
}: InsightPeriodPayload): Promise<void> {
11
  const { start, end } = appDayWindow(new Date(periodStart));
12

13
  const moods = await prisma.mood.findMany({
14
    where: { userId, moment: { gte: start, lt: end } },
15
    orderBy: { moment: "asc" },
16
    select: {
17
      energyLevel: true,
18
      stressLevel: true,
19
      anxietyLevel: true,
20
      selectedMood: true,
21
      moment: true,
22
    },
23
  });
24

25
  if (moods.length === 0) return;
26

27
  const avgEnergy = parseFloat(avg(moods.map((m) => m.energyLevel)).toFixed(2));
28
  const avgStress = parseFloat(avg(moods.map((m) => m.stressLevel)).toFixed(2));
29
  const avgAnxiety = parseFloat(
30
    avg(moods.map((m) => m.anxietyLevel)).toFixed(2),
31
  );
32
  const peak = Math.max(...moods.map((m) => m.energyLevel));
33
  const trough = Math.min(...moods.map((m) => m.energyLevel));
34

35
  // Trend across the day: compare first half vs second half entries
36
  // Not that great, but it works
37
  const mid = Math.floor(moods.length / 2);
38
  const morningAvg =
39
    moods.length > 1
40
      ? avg(moods.slice(0, mid).map((m) => m.energyLevel))
41
      : null;
42

43
  const eveningAvg =
44
    moods.length > 1 ? avg(moods.slice(mid).map((m) => m.energyLevel)) : null;
45

46
  const dayTrend =
47
    morningAvg && eveningAvg
48
      ? parseFloat((((eveningAvg - morningAvg) / morningAvg) * 100).toFixed(1))
49
      : null;
50

51
  await prisma.insight.upsert({
52
    where: {
53
      userId_type_periodStart: {
54
        userId,
55
        type: InsightType.DAILY_ENERGY,
56
        periodStart: start,
57
      },
58
    },
59
    update: {
60
      body: buildBody(avgEnergy, dayTrend),
61
      metadata: {
62
        avgEnergy,
63
        avgStress,
64
        avgAnxiety,
65
        peak,
66
        trough,
67
        dayTrend,
68
        entryCount: moods.length,
69
      },
70
      generatedAt: new Date(),
71
    },
72
    create: {
73
      userId,
74
      type: InsightType.DAILY_ENERGY,
75
      period: InsightPeriod.DAILY,
76
      title: "Energia do Dia",
77
      body: buildBody(avgEnergy, dayTrend),
78
      metadata: {
79
        avgEnergy,
80
        avgStress,
81
        avgAnxiety,
82
        peak,
83
        trough,
84
        dayTrend,
85
        entryCount: moods.length,
86
      },
87
      periodStart: start,
88
      periodEnd: end,
89
    },
90
  });
91
}
92

93
function buildBody(avgEnergy: number, dayTrend: number | null): string {
94
  const level = avgEnergy >= 7 ? "alta" : avgEnergy >= 4 ? "moderada" : "baixa";
95
  if (dayTrend === null) return `Energia ${level} hoje.`;
96
  if (dayTrend > 10)
97
    return `Energia ${level} hoje, com melhora ao longo do dia.`;
98
  if (dayTrend < -10)
99
    return `Energia ${level} hoje, com queda ao longo do dia.`;
100
  return `Energia ${level} e estável ao longo do dia.`;
101
}