eletrotupi / tcc/ api/src/lib/queue/processors/insights/dailySleep.ts68b32b6167f98c0777e1cc816c4065363f24fba4
3.1 KB | 123 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 { logger } from "@app/lib/logger";
5
import { formatHours, formatDiff } from "@app/utils/calc";
6
import {
7
  appDayWindow,
8
  previousAppDay,
9
  DAY_WINDOW_LABEL,
10
} from "@app/lib/queue/processors/insights/utils";
11

12
type SleepRecordEntry = { average: number };
13

14
export type DailySleepMetadata = {
15
  totalHours: number;
16
  diffMinutes: number | null;
17
  diffStr: string;
18
  sessionCount: number;
19
  napStr: string;
20
  hadNaps: boolean;
21
  windowLabel: string;
22
};
23

24
export function computeDailySleep(
25
  todayRecords: SleepRecordEntry[],
26
  yesterdayRecords: SleepRecordEntry[],
27
): { metadata: DailySleepMetadata } {
28
  // average is stored in hours
29
  // sum across all records for the day
30
  const totalToday = todayRecords.reduce((sum, r) => sum + r.average, 0);
31
  const totalYesterday =
32
    yesterdayRecords.length > 0
33
      ? yesterdayRecords.reduce((sum, r) => sum + r.average, 0)
34
      : null;
35

36
  const diffMinutes =
37
    totalYesterday !== null
38
      ? Math.round((totalToday - totalYesterday) * 60)
39
      : null;
40

41
  const sessionCount = todayRecords.length; // 1 = normal night, 2+ = nap(s)
42

43
  const diffStr =
44
    diffMinutes !== null && Math.abs(diffMinutes) >= 5
45
      ? `${formatDiff(diffMinutes)} que ontem`
46
      : "";
47

48
  const napStr =
49
    sessionCount > 1
50
      ? `incluindo ${sessionCount - 1} cochilo${sessionCount > 2 ? "s" : ""}`
51
      : "";
52

53
  const metadata: DailySleepMetadata = {
54
    totalHours: totalToday,
55
    diffMinutes,
56
    diffStr,
57
    sessionCount,
58
    napStr,
59
    hadNaps: sessionCount > 1,
60
    windowLabel: DAY_WINDOW_LABEL,
61
  };
62

63
  return { metadata };
64
}
65

66
export function dailySleepBody(total: number): string {
67
  const totalStr = formatHours(total);
68

69
  return `${totalStr} de sono hoje`;
70
}
71

72
export async function processDailySleep({
73
  userId,
74
  periodStart,
75
}: InsightPeriodPayload): Promise<void> {
76
  const { start: today, end: tomorrow } = appDayWindow(new Date(periodStart));
77
  const yesterday = previousAppDay(today);
78

79
  const [todayRecords, yesterdayRecords] = await Promise.all([
80
    prisma.sleepRecord.findMany({
81
      where: { userId, date: { gte: today, lt: tomorrow } },
82
      select: { average: true },
83
    }),
84
    prisma.sleepRecord.findMany({
85
      where: { userId, date: { gte: yesterday, lt: today } },
86
      select: { average: true },
87
    }),
88
  ]);
89

90
  logger.debug(
91
    { todayRecords, yesterdayRecords },
92
    "[insights] daily sleep records",
93
  );
94

95
  if (todayRecords.length === 0) return;
96

97
  const { metadata } = computeDailySleep(todayRecords, yesterdayRecords);
98

99
  await prisma.insight.upsert({
100
    where: {
101
      userId_type_periodStart: {
102
        userId,
103
        type: InsightType.DAILY_SLEEP,
104
        periodStart: today,
105
      },
106
    },
107
    update: {
108
      body: dailySleepBody(metadata.totalHours),
109
      metadata,
110
      generatedAt: new Date(),
111
    },
112
    create: {
113
      userId,
114
      type: InsightType.DAILY_SLEEP,
115
      period: InsightPeriod.DAILY,
116
      title: "Sono",
117
      body: dailySleepBody(metadata.totalHours),
118
      metadata,
119
      periodStart: today,
120
      periodEnd: tomorrow,
121
    },
122
  });
123
}