From b8918889a1712fe9252aa0ae1e1b765adb1660c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Mon, 1 Jun 2020 20:34:59 +0200 Subject: [PATCH] refactor(server): introduce TimePresets for reports --- src/listens/dto/get-listens.dto.ts | 13 +++--- src/listens/listens.service.ts | 4 +- src/reports/interval.d.ts | 4 ++ src/reports/reports.service.ts | 70 ++++++++++++++++++++++++++++-- src/reports/timePreset.enum.ts | 9 ++++ 5 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 src/reports/interval.d.ts create mode 100644 src/reports/timePreset.enum.ts diff --git a/src/listens/dto/get-listens.dto.ts b/src/listens/dto/get-listens.dto.ts index 6d45a96..3f596d1 100644 --- a/src/listens/dto/get-listens.dto.ts +++ b/src/listens/dto/get-listens.dto.ts @@ -1,12 +1,13 @@ -import { IsOptional, ValidateNested, IsISO8601 } from "class-validator"; +import { IsDate, IsOptional, ValidateNested } from "class-validator"; +import { Interval } from "date-fns"; import { User } from "src/users/user.entity"; // tslint:disable-next-line: max-classes-per-file -export class GetListensFilterTimeDto { - @IsISO8601() - start: string; - @IsISO8601() - end: string; +export class GetListensFilterTimeDto implements Interval { + @IsDate() + start: Date; + @IsDate() + end: Date; } // tslint:disable-next-line: max-classes-per-file diff --git a/src/listens/listens.service.ts b/src/listens/listens.service.ts index 2aa3332..f0e4453 100644 --- a/src/listens/listens.service.ts +++ b/src/listens/listens.service.ts @@ -60,8 +60,8 @@ export class ListensService { queryBuilder = queryBuilder.andWhere( "l.playedAt BETWEEN :timeStart AND :timeEnd", { - timeStart: parseISO(filter.time.start), - timeEnd: parseISO(filter.time.end), + timeStart: filter.time.start, + timeEnd: filter.time.end, } ); } diff --git a/src/reports/interval.d.ts b/src/reports/interval.d.ts new file mode 100644 index 0000000..c3ecbd1 --- /dev/null +++ b/src/reports/interval.d.ts @@ -0,0 +1,4 @@ +export interface Interval { + start: Date; + end: Date; +} diff --git a/src/reports/reports.service.ts b/src/reports/reports.service.ts index a183255..58eb9b2 100644 --- a/src/reports/reports.service.ts +++ b/src/reports/reports.service.ts @@ -1,21 +1,25 @@ -import { Injectable } from "@nestjs/common"; +import { BadRequestException, Injectable } from "@nestjs/common"; import { eachDayOfInterval, eachMonthOfInterval, eachWeekOfInterval, eachYearOfInterval, + endOfDay, formatISO, - Interval, isSameDay, isSameMonth, isSameWeek, isSameYear, parseISO, + parseJSON, + startOfDay, + sub, } from "date-fns"; import { ListensService } from "../listens/listens.service"; import { GetListenReportDto } from "./dto/get-listen-report.dto"; import { ListenReportDto } from "./dto/listen-report.dto"; import { Timeframe } from "./timeframe.enum"; +import { TimePreset } from "./timePreset.enum"; const timeframeToDateFns: { [x in Timeframe]: { @@ -41,6 +45,16 @@ const timeframeToDateFns: { }, }; +const timePresetToDays: { [x in TimePreset]: number } = { + [TimePreset.LAST_7_DAYS]: 7, + [TimePreset.LAST_30_DAYS]: 30, + [TimePreset.LAST_90_DAYS]: 90, + [TimePreset.LAST_180_DAYS]: 180, + [TimePreset.LAST_365_DAYS]: 365, + [TimePreset.ALL_TIME]: 0, // Not used for this + [TimePreset.CUSTOM]: 0, // Not used for this +}; + @Injectable() export class ReportsService { constructor(private readonly listensService: ListensService) {} @@ -48,9 +62,16 @@ export class ReportsService { async getListens(options: GetListenReportDto): Promise { const { user, timeFrame, timeStart, timeEnd } = options; + // Function should eventually be rewritten to accept a timepreset + const interval = this.getIntervalFromPreset({ + timePreset: TimePreset.CUSTOM, + customTimeStart: timeStart, + customTimeEnd: timeEnd, + }); + const { items: listens } = await this.listensService.getListens({ user, - filter: { time: { start: timeStart, end: timeEnd } }, + filter: { time: interval }, page: 1, limit: 10000000, }); @@ -70,4 +91,47 @@ export class ReportsService { return { items: reportItems, timeStart, timeEnd, timeFrame }; } + private getIntervalFromPreset(options: { + timePreset: TimePreset; + customTimeStart?: string; + customTimeEnd?: string; + }): Interval { + let interval = { + start: startOfDay(new Date()), + end: endOfDay(new Date()), // NOW + }; + + switch (options.timePreset) { + case TimePreset.LAST_7_DAYS: + case TimePreset.LAST_30_DAYS: + case TimePreset.LAST_90_DAYS: + case TimePreset.LAST_180_DAYS: + case TimePreset.LAST_365_DAYS: { + interval.start = startOfDay( + sub(interval.start, { days: timePresetToDays[options.timePreset] }) + ); + break; + } + + case TimePreset.ALL_TIME: { + interval.start = new Date(0); // Start of epoch + break; + } + + case TimePreset.CUSTOM: { + if (!options.customTimeStart && !options.customTimeEnd) { + throw new BadRequestException("MissingCustomTime"); + } + + interval = { + start: parseJSON(options.customTimeStart), + end: parseJSON(options.customTimeEnd), + }; + + break; + } + } + + return interval; + } } diff --git a/src/reports/timePreset.enum.ts b/src/reports/timePreset.enum.ts new file mode 100644 index 0000000..d358523 --- /dev/null +++ b/src/reports/timePreset.enum.ts @@ -0,0 +1,9 @@ +export enum TimePreset { + LAST_7_DAYS = "last_7_days", + LAST_30_DAYS = "last_30_days", + LAST_90_DAYS = "last_90_days", + LAST_180_DAYS = "last_180_days", + LAST_365_DAYS = "last_365_days", + ALL_TIME = "all_time", + CUSTOM = "custom", +}