| 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 | export async function processDailySleep({
|
| 13 | userId,
|
| 14 | periodStart,
|
| 15 | }: InsightPeriodPayload): Promise<void> {
|
| 16 | const { start: today, end: tomorrow } = appDayWindow(new Date(periodStart));
|
| 17 | const yesterday = previousAppDay(today);
|
| 18 |
|
| 19 | const [todayRecords, yesterdayRecords] = await Promise.all([
|
| 20 | prisma.sleepRecord.findMany({
|
| 21 | where: { userId, date: { gte: today, lt: tomorrow } },
|
| 22 | select: { average: true },
|
| 23 | }),
|
| 24 | prisma.sleepRecord.findMany({
|
| 25 | where: { userId, date: { gte: yesterday, lt: today } },
|
| 26 | select: { average: true },
|
| 27 | }),
|
| 28 | ]);
|
| 29 |
|
| 30 | logger.debug(
|
| 31 | { todayRecords, yesterdayRecords },
|
| 32 | "[insights] daily sleep records",
|
| 33 | );
|
| 34 |
|
| 35 | if (todayRecords.length === 0) return;
|
| 36 |
|
| 37 | // average is stored in hours
|
| 38 | // sum across all records for the day
|
| 39 | const totalToday = todayRecords.reduce(
|
| 40 | (sum: number, r: { average: number }) => sum + r.average,
|
| 41 | 0,
|
| 42 | );
|
| 43 | const totalYesterday =
|
| 44 | yesterdayRecords.length > 0
|
| 45 | ? yesterdayRecords.reduce(
|
| 46 | (sum: number, r: { average: number }) => sum + r.average,
|
| 47 | 0,
|
| 48 | )
|
| 49 | : null;
|
| 50 |
|
| 51 | const diffMinutes =
|
| 52 | totalYesterday !== null
|
| 53 | ? Math.round((totalToday - totalYesterday) * 60)
|
| 54 | : null;
|
| 55 |
|
| 56 | const sessionCount = todayRecords.length; // 1 = normal night, 2+ = nap(s)
|
| 57 |
|
| 58 | const diffStr =
|
| 59 | diffMinutes !== null && Math.abs(diffMinutes) >= 5
|
| 60 | ? `${formatDiff(diffMinutes)} que ontem`
|
| 61 | : "";
|
| 62 |
|
| 63 | const napStr =
|
| 64 | sessionCount > 1
|
| 65 | ? `incluindo ${sessionCount - 1} cochilo${sessionCount > 2 ? "s" : ""}`
|
| 66 | : "";
|
| 67 |
|
| 68 | const metadata = {
|
| 69 | totalHours: totalToday,
|
| 70 | diffMinutes,
|
| 71 | diffStr,
|
| 72 | sessionCount,
|
| 73 | napStr,
|
| 74 | hadNaps: sessionCount > 1,
|
| 75 | windowLabel: DAY_WINDOW_LABEL,
|
| 76 | };
|
| 77 |
|
| 78 | await prisma.insight.upsert({
|
| 79 | where: {
|
| 80 | userId_type_periodStart: {
|
| 81 | userId,
|
| 82 | type: InsightType.DAILY_SLEEP,
|
| 83 | periodStart: today,
|
| 84 | },
|
| 85 | },
|
| 86 | update: {
|
| 87 | body: buildBody(totalToday),
|
| 88 | metadata,
|
| 89 | generatedAt: new Date(),
|
| 90 | },
|
| 91 | create: {
|
| 92 | userId,
|
| 93 | type: InsightType.DAILY_SLEEP,
|
| 94 | period: InsightPeriod.DAILY,
|
| 95 | title: "Sono",
|
| 96 | body: buildBody(totalToday),
|
| 97 | metadata,
|
| 98 | periodStart: today,
|
| 99 | periodEnd: tomorrow,
|
| 100 | },
|
| 101 | });
|
| 102 | }
|
| 103 |
|
| 104 | function buildBody(total: number): string {
|
| 105 | const totalStr = formatHours(total);
|
| 106 |
|
| 107 | return `${totalStr} de sono hoje`;
|
| 108 | }
|