refactor(server): introduce TimePresets for reports

This commit is contained in:
Julian Tölle 2020-06-01 20:34:59 +02:00
parent 6f8fc0265a
commit b8918889a1
5 changed files with 89 additions and 11 deletions

View file

@ -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"; import { User } from "src/users/user.entity";
// tslint:disable-next-line: max-classes-per-file // tslint:disable-next-line: max-classes-per-file
export class GetListensFilterTimeDto { export class GetListensFilterTimeDto implements Interval {
@IsISO8601() @IsDate()
start: string; start: Date;
@IsISO8601() @IsDate()
end: string; end: Date;
} }
// tslint:disable-next-line: max-classes-per-file // tslint:disable-next-line: max-classes-per-file

View file

@ -60,8 +60,8 @@ export class ListensService {
queryBuilder = queryBuilder.andWhere( queryBuilder = queryBuilder.andWhere(
"l.playedAt BETWEEN :timeStart AND :timeEnd", "l.playedAt BETWEEN :timeStart AND :timeEnd",
{ {
timeStart: parseISO(filter.time.start), timeStart: filter.time.start,
timeEnd: parseISO(filter.time.end), timeEnd: filter.time.end,
} }
); );
} }

4
src/reports/interval.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
export interface Interval {
start: Date;
end: Date;
}

View file

@ -1,21 +1,25 @@
import { Injectable } from "@nestjs/common"; import { BadRequestException, Injectable } from "@nestjs/common";
import { import {
eachDayOfInterval, eachDayOfInterval,
eachMonthOfInterval, eachMonthOfInterval,
eachWeekOfInterval, eachWeekOfInterval,
eachYearOfInterval, eachYearOfInterval,
endOfDay,
formatISO, formatISO,
Interval,
isSameDay, isSameDay,
isSameMonth, isSameMonth,
isSameWeek, isSameWeek,
isSameYear, isSameYear,
parseISO, parseISO,
parseJSON,
startOfDay,
sub,
} from "date-fns"; } from "date-fns";
import { ListensService } from "../listens/listens.service"; import { ListensService } from "../listens/listens.service";
import { GetListenReportDto } from "./dto/get-listen-report.dto"; import { GetListenReportDto } from "./dto/get-listen-report.dto";
import { ListenReportDto } from "./dto/listen-report.dto"; import { ListenReportDto } from "./dto/listen-report.dto";
import { Timeframe } from "./timeframe.enum"; import { Timeframe } from "./timeframe.enum";
import { TimePreset } from "./timePreset.enum";
const timeframeToDateFns: { const timeframeToDateFns: {
[x in Timeframe]: { [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() @Injectable()
export class ReportsService { export class ReportsService {
constructor(private readonly listensService: ListensService) {} constructor(private readonly listensService: ListensService) {}
@ -48,9 +62,16 @@ export class ReportsService {
async getListens(options: GetListenReportDto): Promise<ListenReportDto> { async getListens(options: GetListenReportDto): Promise<ListenReportDto> {
const { user, timeFrame, timeStart, timeEnd } = options; 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({ const { items: listens } = await this.listensService.getListens({
user, user,
filter: { time: { start: timeStart, end: timeEnd } }, filter: { time: interval },
page: 1, page: 1,
limit: 10000000, limit: 10000000,
}); });
@ -70,4 +91,47 @@ export class ReportsService {
return { items: reportItems, timeStart, timeEnd, timeFrame }; 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;
}
} }

View file

@ -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",
}