eletrotupi / tcc / api/src/lib/queue/processors/insights/moodTrend.ts master
2.4 KB Raw
import { prisma } from "@app/lib/prisma";
import { InsightType, InsightPeriod, BaseMoodOption } from "@prisma/client";
import { InsightPeriodPayload } from "@app/lib/queue/types";

const MOOD_SCORE: Record<BaseMoodOption, number> = {
  GREAT: 5,
  GOOD: 4,
  NEUTRAL: 3,
  SAD: 2,
  ANGRY: 1,
};

const avg = (values: number[]) =>
  values.reduce((a, b) => a + b, 0) / values.length;

export async function processMoodTrend({
  userId,
  periodStart,
  periodEnd,
}: InsightPeriodPayload): Promise<void> {
  const start = new Date(periodStart);
  const end = new Date(periodEnd);

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

  if (moods.length < 3) return;

  const scores = moods.map((m) => MOOD_SCORE[m.selectedMood]);
  const mid = Math.floor(scores.length / 2);
  const avgFirst = avg(scores.slice(0, mid));
  const avgSecond = avg(scores.slice(mid));
  const delta = Math.round(((avgSecond - avgFirst) / avgFirst) * 100);

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

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

  await prisma.insight.upsert({
    where: {
      userId_type_periodStart: {
        userId,
        type: InsightType.MOOD_TREND,
        periodStart: start,
      },
    },
    update: {
      body: buildBody(delta),
      metadata: { delta, avgFirst, avgSecond, dominantMood, avgEnergy },
      generatedAt: new Date(),
    },
    create: {
      userId,
      type: InsightType.MOOD_TREND,
      period: InsightPeriod.WEEKLY,
      title: "Tendência de Humor",
      body: buildBody(delta),
      metadata: { delta, avgFirst, avgSecond, dominantMood, avgEnergy },
      periodStart: start,
      periodEnd: end,
    },
  });
}

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