eletrotupi / tcc/ api/src/lib/queue/processors/insights/triggerPattern.ts097962129b7a820be8edd48f6ebd3213a58486f9
2.4 KB | 96 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
  OTHER: "outros",
12
};
13

14
export async function processTriggerPattern({
15
  userId,
16
  periodStart,
17
  periodEnd,
18
}: InsightPeriodPayload): Promise<void> {
19
  const start = new Date(periodStart);
20
  const end = new Date(periodEnd);
21

22
  const triggers = await prisma.trigger.findMany({
23
    where: { userId, moment: { gte: start, lte: end } },
24
    select: { category: true },
25
  });
26

27
  if (triggers.length === 0) return;
28

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

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

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

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

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

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

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