mirror of
https://github.com/apricote/Listory.git
synced 2026-01-13 21:21:02 +00:00
feat: introduce new report "Top Albums"
This commit is contained in:
parent
09858076bf
commit
9896ea31ff
16 changed files with 246 additions and 9 deletions
|
|
@ -20,10 +20,10 @@ export class Album {
|
|||
|
||||
@ManyToMany((type) => Artist, (artist) => artist.albums)
|
||||
@JoinTable({ name: "album_artists" })
|
||||
artists: Artist[];
|
||||
artists?: Artist[];
|
||||
|
||||
@OneToMany((type) => Track, (track) => track.album)
|
||||
tracks: Track[];
|
||||
tracks?: Track[];
|
||||
|
||||
@Column((type) => SpotifyLibraryDetails)
|
||||
spotify: SpotifyLibraryDetails;
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ export class Track {
|
|||
name: string;
|
||||
|
||||
@ManyToOne((type) => Album, (album) => album.tracks)
|
||||
album: Album;
|
||||
album?: Album;
|
||||
|
||||
@ManyToMany((type) => Artist)
|
||||
@JoinTable({ name: "track_artists" })
|
||||
artists: Artist[];
|
||||
artists?: Artist[];
|
||||
|
||||
@Column((type) => SpotifyLibraryDetails)
|
||||
spotify?: SpotifyLibraryDetails;
|
||||
|
|
|
|||
10
src/reports/dto/get-top-albums-report.dto.ts
Normal file
10
src/reports/dto/get-top-albums-report.dto.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { ValidateNested } from "class-validator";
|
||||
import { User } from "../../users/user.entity";
|
||||
import { ReportTimeDto } from "./report-time.dto";
|
||||
|
||||
export class GetTopAlbumsReportDto {
|
||||
user: User;
|
||||
|
||||
@ValidateNested()
|
||||
time: ReportTimeDto;
|
||||
}
|
||||
8
src/reports/dto/top-albums-report.dto.ts
Normal file
8
src/reports/dto/top-albums-report.dto.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { Album } from "../../music-library/album.entity";
|
||||
|
||||
export class TopAlbumsReportDto {
|
||||
items: {
|
||||
album: Album;
|
||||
count: number;
|
||||
}[];
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import { ReqUser } from "../auth/decorators/req-user.decorator";
|
|||
import { User } from "../users/user.entity";
|
||||
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 { ReportsService } from "./reports.service";
|
||||
import { Timeframe } from "./timeframe.enum";
|
||||
|
|
@ -30,4 +31,13 @@ export class ReportsController {
|
|||
): Promise<TopArtistsReportDto> {
|
||||
return this.reportsService.getTopArtists({ user, time });
|
||||
}
|
||||
|
||||
@Get("top-albums")
|
||||
@AuthAccessToken()
|
||||
async getTopAlbums(
|
||||
@Query() time: ReportTimeDto,
|
||||
@ReqUser() user: User
|
||||
): Promise<TopAlbumsReportDto> {
|
||||
return this.reportsService.getTopAlbums({ user, time });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ import {
|
|||
} from "date-fns";
|
||||
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 { ListenReportDto } from "./dto/listen-report.dto";
|
||||
import { TopAlbumsReportDto } from "./dto/top-albums-report.dto";
|
||||
import { TopArtistsReportDto } from "./dto/top-artists-report.dto";
|
||||
import { Interval } from "./interval";
|
||||
import { Timeframe } from "./timeframe.enum";
|
||||
|
|
@ -124,6 +126,55 @@ export class ReportsService {
|
|||
};
|
||||
}
|
||||
|
||||
async getTopAlbums(
|
||||
options: GetTopAlbumsReportDto
|
||||
): Promise<TopAlbumsReportDto> {
|
||||
const { user, time: timePreset } = options;
|
||||
|
||||
const interval = this.getIntervalFromPreset(timePreset);
|
||||
|
||||
const getListensQB = () =>
|
||||
this.listensService
|
||||
.getScopedQueryBuilder()
|
||||
.byUser(user)
|
||||
.duringInterval(interval);
|
||||
|
||||
const [rawAlbumsWithCount, rawAlbumDetails] = await Promise.all([
|
||||
getListensQB()
|
||||
.leftJoin("listen.track", "track")
|
||||
.leftJoinAndSelect("track.album", "album")
|
||||
.groupBy("album.id")
|
||||
.select("album.id")
|
||||
.addSelect("count(*) as listens")
|
||||
.orderBy("listens", "DESC")
|
||||
.getRawMany(),
|
||||
|
||||
// Because of the GROUP BY required to calculate the count we can
|
||||
// not properly join the album relations in one query
|
||||
getListensQB()
|
||||
.leftJoinAndSelect("listen.track", "track")
|
||||
.leftJoinAndSelect("track.album", "album")
|
||||
.leftJoinAndSelect("album.artists", "artists")
|
||||
.distinctOn(["album.id"])
|
||||
.getMany(),
|
||||
]);
|
||||
|
||||
const albumDetails = rawAlbumDetails
|
||||
.map((listen) => listen.track.album)
|
||||
.filter((album) => album && album.artists); // Make sure entities are set
|
||||
|
||||
const items: TopAlbumsReportDto["items"] = rawAlbumsWithCount.map(
|
||||
(data) => ({
|
||||
count: Number.parseInt(data.listens, 10),
|
||||
album: albumDetails.find((album) => album.id === data.album_id),
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
items,
|
||||
};
|
||||
}
|
||||
|
||||
private getIntervalFromPreset(options: {
|
||||
timePreset: TimePreset;
|
||||
customTimeStart?: string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue