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