mirror of
https://github.com/apricote/Listory.git
synced 2026-01-13 21:21:02 +00:00
feat(api): add listen report endpoint
This commit is contained in:
parent
ddcdfff89b
commit
3828b841c2
9 changed files with 179 additions and 0 deletions
|
|
@ -11,6 +11,7 @@ import { SourcesModule } from "./sources/sources.module";
|
||||||
import { UsersModule } from "./users/users.module";
|
import { UsersModule } from "./users/users.module";
|
||||||
import { ConfigModule } from "./config/config.module";
|
import { ConfigModule } from "./config/config.module";
|
||||||
import { HealthCheckModule } from "./health-check/health-check.module";
|
import { HealthCheckModule } from "./health-check/health-check.module";
|
||||||
|
import { ReportsModule } from "./reports/reports.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -28,6 +29,7 @@ import { HealthCheckModule } from "./health-check/health-check.module";
|
||||||
MusicLibraryModule,
|
MusicLibraryModule,
|
||||||
ListensModule,
|
ListensModule,
|
||||||
HealthCheckModule,
|
HealthCheckModule,
|
||||||
|
ReportsModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
||||||
16
src/reports/dto/get-listen-report.dto.ts
Normal file
16
src/reports/dto/get-listen-report.dto.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { IsEnum, IsISO8601 } from "class-validator";
|
||||||
|
import { User } from "../../users/user.entity";
|
||||||
|
import { Timeframe } from "../timeframe.enum";
|
||||||
|
|
||||||
|
export class GetListenReportDto {
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@IsEnum(Timeframe)
|
||||||
|
timeFrame: Timeframe;
|
||||||
|
|
||||||
|
@IsISO8601()
|
||||||
|
timeStart: string;
|
||||||
|
|
||||||
|
@IsISO8601()
|
||||||
|
timeEnd: string;
|
||||||
|
}
|
||||||
14
src/reports/dto/listen-report.dto.ts
Normal file
14
src/reports/dto/listen-report.dto.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Timeframe } from "../timeframe.enum";
|
||||||
|
|
||||||
|
export class ListenReportDto {
|
||||||
|
items: {
|
||||||
|
date: string;
|
||||||
|
count: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
timeFrame: Timeframe;
|
||||||
|
|
||||||
|
timeStart: string;
|
||||||
|
|
||||||
|
timeEnd: string;
|
||||||
|
}
|
||||||
18
src/reports/reports.controller.spec.ts
Normal file
18
src/reports/reports.controller.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { ReportsController } from './reports.controller';
|
||||||
|
|
||||||
|
describe('Reports Controller', () => {
|
||||||
|
let controller: ReportsController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [ReportsController],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<ReportsController>(ReportsController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
21
src/reports/reports.controller.ts
Normal file
21
src/reports/reports.controller.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Controller, Get, Query } from "@nestjs/common";
|
||||||
|
import { Auth } from "src/auth/decorators/auth.decorator";
|
||||||
|
import { ReqUser } from "../auth/decorators/req-user.decorator";
|
||||||
|
import { User } from "../users/user.entity";
|
||||||
|
import { GetListenReportDto } from "./dto/get-listen-report.dto";
|
||||||
|
import { ListenReportDto } from "./dto/listen-report.dto";
|
||||||
|
import { ReportsService } from "./reports.service";
|
||||||
|
|
||||||
|
@Controller("api/v1/reports")
|
||||||
|
export class ReportsController {
|
||||||
|
constructor(private readonly reportsService: ReportsService) {}
|
||||||
|
|
||||||
|
@Get("listens")
|
||||||
|
@Auth()
|
||||||
|
async getListens(
|
||||||
|
@Query() options: Omit<GetListenReportDto, "user">,
|
||||||
|
@ReqUser() user: User
|
||||||
|
): Promise<ListenReportDto> {
|
||||||
|
return this.reportsService.getListens({ ...options, user });
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/reports/reports.module.ts
Normal file
11
src/reports/reports.module.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { ReportsService } from "./reports.service";
|
||||||
|
import { ReportsController } from "./reports.controller";
|
||||||
|
import { ListensModule } from "src/listens/listens.module";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ListensModule],
|
||||||
|
providers: [ReportsService],
|
||||||
|
controllers: [ReportsController],
|
||||||
|
})
|
||||||
|
export class ReportsModule {}
|
||||||
18
src/reports/reports.service.spec.ts
Normal file
18
src/reports/reports.service.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { ReportsService } from './reports.service';
|
||||||
|
|
||||||
|
describe('ReportsService', () => {
|
||||||
|
let service: ReportsService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [ReportsService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<ReportsService>(ReportsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
73
src/reports/reports.service.ts
Normal file
73
src/reports/reports.service.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import {
|
||||||
|
eachDayOfInterval,
|
||||||
|
eachMonthOfInterval,
|
||||||
|
eachWeekOfInterval,
|
||||||
|
eachYearOfInterval,
|
||||||
|
formatISO,
|
||||||
|
Interval,
|
||||||
|
isSameDay,
|
||||||
|
isSameMonth,
|
||||||
|
isSameWeek,
|
||||||
|
isSameYear,
|
||||||
|
parseISO,
|
||||||
|
} 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";
|
||||||
|
|
||||||
|
const timeframeToDateFns: {
|
||||||
|
[x in Timeframe]: {
|
||||||
|
eachOfInterval: (interval: Interval) => Date[];
|
||||||
|
isSame: (dateLeft: Date, dateRight: Date) => boolean;
|
||||||
|
};
|
||||||
|
} = {
|
||||||
|
[Timeframe.Day]: {
|
||||||
|
eachOfInterval: eachDayOfInterval,
|
||||||
|
isSame: isSameDay,
|
||||||
|
},
|
||||||
|
[Timeframe.Week]: {
|
||||||
|
eachOfInterval: eachWeekOfInterval,
|
||||||
|
isSame: isSameWeek,
|
||||||
|
},
|
||||||
|
[Timeframe.Month]: {
|
||||||
|
eachOfInterval: eachMonthOfInterval,
|
||||||
|
isSame: isSameMonth,
|
||||||
|
},
|
||||||
|
[Timeframe.Year]: {
|
||||||
|
eachOfInterval: eachYearOfInterval,
|
||||||
|
isSame: isSameYear,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ReportsService {
|
||||||
|
constructor(private readonly listensService: ListensService) {}
|
||||||
|
|
||||||
|
async getListens(options: GetListenReportDto): Promise<ListenReportDto> {
|
||||||
|
const { user, timeFrame, timeStart, timeEnd } = options;
|
||||||
|
|
||||||
|
const { items: listens } = await this.listensService.getListens({
|
||||||
|
user,
|
||||||
|
filter: { time: { start: timeStart, end: timeEnd } },
|
||||||
|
page: 1,
|
||||||
|
limit: 10000000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const reportInterval: Interval = {
|
||||||
|
start: parseISO(timeStart),
|
||||||
|
end: parseISO(timeEnd),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { eachOfInterval, isSame } = timeframeToDateFns[timeFrame];
|
||||||
|
|
||||||
|
const reportItems = eachOfInterval(reportInterval).map((date) => {
|
||||||
|
const count = listens.filter((listen) => isSame(date, listen.playedAt))
|
||||||
|
.length;
|
||||||
|
return { date: formatISO(date), count };
|
||||||
|
});
|
||||||
|
|
||||||
|
return { items: reportItems, timeStart, timeEnd, timeFrame };
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/reports/timeframe.enum.ts
Normal file
6
src/reports/timeframe.enum.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export enum Timeframe {
|
||||||
|
Day = "day",
|
||||||
|
Week = "week",
|
||||||
|
Month = "month",
|
||||||
|
Year = "year",
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue