feat: add top tracks report

This commit is contained in:
Julian Tölle 2021-05-22 14:57:28 +02:00
parent 60dc25f15e
commit 51fd78f6d9
11 changed files with 230 additions and 0 deletions

View file

@ -0,0 +1,10 @@
import { ValidateNested } from "class-validator";
import { User } from "../../users/user.entity";
import { ReportTimeDto } from "./report-time.dto";
export class GetTopTracksReportDto {
user: User;
@ValidateNested()
time: ReportTimeDto;
}

View file

@ -0,0 +1,8 @@
import { Track } from "../../music-library/track.entity";
export class TopTracksReportDto {
items: {
track: Track;
count: number;
}[];
}

View file

@ -6,6 +6,7 @@ import { ListenReportDto } from "./dto/listen-report.dto";
import { ReportTimeDto } from "./dto/report-time.dto";
import { TopAlbumsReportDto } from "./dto/top-albums-report.dto";
import { TopArtistsReportDto } from "./dto/top-artists-report.dto";
import { TopTracksReportDto } from "./dto/top-tracks-report.dto";
import { ReportsService } from "./reports.service";
import { Timeframe } from "./timeframe.enum";
@ -40,4 +41,13 @@ export class ReportsController {
): Promise<TopAlbumsReportDto> {
return this.reportsService.getTopAlbums({ user, time });
}
@Get("top-tracks")
@AuthAccessToken()
async getTopTracks(
@Query() time: ReportTimeDto,
@ReqUser() user: User
): Promise<TopTracksReportDto> {
return this.reportsService.getTopTracks({ user, time });
}
}

View file

@ -18,9 +18,11 @@ import { ListensService } from "../listens/listens.service";
import { GetListenReportDto } from "./dto/get-listen-report.dto";
import { GetTopAlbumsReportDto } from "./dto/get-top-albums-report.dto";
import { GetTopArtistsReportDto } from "./dto/get-top-artists-report.dto";
import { GetTopTracksReportDto } from "./dto/get-top-tracks-report.dto";
import { ListenReportDto } from "./dto/listen-report.dto";
import { TopAlbumsReportDto } from "./dto/top-albums-report.dto";
import { TopArtistsReportDto } from "./dto/top-artists-report.dto";
import { TopTracksReportDto } from "./dto/top-tracks-report.dto";
import { Interval } from "./interval";
import { Timeframe } from "./timeframe.enum";
import { TimePreset } from "./timePreset.enum";
@ -175,6 +177,53 @@ export class ReportsService {
};
}
async getTopTracks(
options: GetTopTracksReportDto
): Promise<TopTracksReportDto> {
const { user, time: timePreset } = options;
const interval = this.getIntervalFromPreset(timePreset);
const getListensQB = () =>
this.listensService
.getScopedQueryBuilder()
.byUser(user)
.duringInterval(interval);
const [rawTracksWithCount, rawTrackDetails] = await Promise.all([
getListensQB()
.leftJoin("listen.track", "track")
.groupBy("track.id")
.select("track.*")
.addSelect("count(*) as listens")
.orderBy("listens", "DESC")
.getRawMany(),
// Because of the GROUP BY required to calculate the count we can
// not properly join the artist relations in one query
getListensQB()
.leftJoinAndSelect("listen.track", "track")
.leftJoinAndSelect("track.artists", "artists")
.distinctOn(["track.id"])
.getMany(),
]);
const trackDetails = rawTrackDetails
.map((listen) => listen.track)
.filter((track) => track); // Make sure entities are set
const items: TopTracksReportDto["items"] = rawTracksWithCount.map(
(data) => ({
count: Number.parseInt(data.listens, 10),
track: trackDetails.find((track) => track.id === data.id),
})
);
return {
items,
};
}
private getIntervalFromPreset(options: {
timePreset: TimePreset;
customTimeStart?: string;