eletrotupi / tcc/ api/src/lib/queue/processors/insights/triggerPattern.ts04084150bf5e7e1867221dfcc35651e6d37ad766
2.5 KB | 98 lines Raw
1
import { prisma } from "@app/lib/prisma";
2
import { InsightType, InsightPeriod, TriggerType } from "@prisma/client";
3
import { InsightPeriodPayload } from "@app/lib/queue/types";
4

5
const TRIGGER_LABEL: Record<TriggerType, string> = {
6
  SOCIAL: "social",
7
  WORK: "trabalho",
8
  HEALTH: "saúde",
9
  PHYSICAL: "físico",
10
  FAMILY: "família",
11
  THERAPY: "terapia",
12
  INTERNAL: "interno",
13
  OTHER: "outros",
14
};
15

16
export async function processTriggerPattern({
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 triggers = await prisma.trigger.findMany({
25
    where: { userId, moment: { gte: start, lte: end } },
26
    select: { category: true },
27
  });
28

29
  if (triggers.length === 0) return;
30

31
  // Count by category
32
  const counts = triggers.reduce<Partial<Record<TriggerType, number>>>(
33
    (acc, t) => ({ ...acc, [t.category]: (acc[t.category] ?? 0) + 1 }),
34
    {},
35
  );
36

37
  const sorted = (Object.entries(counts) as [TriggerType, number][]).sort(
38
    (a, b) => b[1] - a[1],
39
  );
40

41
  const [topCategory, topCount] = sorted[0];
42
  const distribution = Object.fromEntries(sorted);
43

44
  await prisma.insight.upsert({
45
    where: {
46
      userId_type_periodStart: {
47
        userId,
48
        type: InsightType.TRIGGER_PATTERN,
49
        periodStart: start,
50
      },
51
    },
52
    update: {
53
      body: buildBody(topCategory, topCount, triggers.length),
54
      metadata: {
55
        topCategory,
56
        topCount,
57
        total: triggers.length,
58
        distribution,
59
      },
60
      generatedAt: new Date(),
61
    },
62
    create: {
63
      userId,
64
      type: InsightType.TRIGGER_PATTERN,
65
      period: InsightPeriod.WEEKLY,
66
      title: "Padrão de Gatilhos",
67
      body: buildBody(topCategory, topCount, triggers.length),
68
      metadata: {
69
        topCategory,
70
        topCount,
71
        total: triggers.length,
72
        distribution,
73
      },
74
      periodStart: start,
75
      periodEnd: end,
76
    },
77
  });
78
}
79

80
function buildBody(top: TriggerType, count: number, total: number): string {
81
  const pct = Math.round((count / total) * 100);
82
  const label = TRIGGER_LABEL[top];
83
  let template = "";
84

85
  if (top == "OTHER") {
86
    template = `
87
    Gatilhos do tipo "${label}" foram os mais frequentes,
88
    representando ${pct}% dos registros do período.
89
  `
90
  } else {
91
    template = `
92
    Gatilhos de ${label} foram os mais frequentes,
93
    representando ${pct}% dos registros do período.
94
  `
95
  }
96

97
  return template.replace(/\s*\n\s*/g, " ").trim();
98
}