Skip to content

Commit

Permalink
Merge pull request #85 from game-node-app/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Lamarcke authored Jul 11, 2024
2 parents 5ae2c13 + 44d2849 commit 21ccf53
Show file tree
Hide file tree
Showing 32 changed files with 562 additions and 86 deletions.
2 changes: 1 addition & 1 deletion server_swagger.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/achievements/achievements.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ export class AchievementsController {
);
}

@Get(":userId/featured")
@Public()
public getFeaturedAchievementForUserId(@Param("userId") userId: string) {
return this.achievementsService.getFeaturedAchievement(userId);
}

@Put("obtained/:id/featured")
public updateFeaturedObtainedAchievement(
@Session() session: SessionContainer,
Expand Down
7 changes: 7 additions & 0 deletions src/achievements/achievements.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ export class AchievementsService {
});
}

async getFeaturedAchievement(userId: string) {
return await this.obtainedAchievementsRepository.findOneByOrFail({
profileUserId: userId,
isFeatured: true,
});
}

async updateFeaturedObtainedAchievement(
userId: string,
achievementId: string,
Expand Down
6 changes: 4 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ import { ImporterModule } from "./importer/importer.module";
import { ConnectionsModule } from "./connections/connections.module";
import { CommentModule } from "./comment/comment.module";
import { ImporterWatchModule } from "./importer/importer-watch/importer-watch.module";
import { ReportModule } from './report/report.module';
import { SuspensionModule } from './suspension/suspension.module';
import { ReportModule } from "./report/report.module";
import { SuspensionModule } from "./suspension/suspension.module";
import { ProfileMetricsModule } from "./profile/profile-statistics/profile-metrics.module";

/**
* Should only be called after 'ConfigModule' is loaded (e.g. in useFactory)
Expand Down Expand Up @@ -159,6 +160,7 @@ function getRedisConfig() {
CollectionsModule,
StatisticsModule,
StatisticsQueueModule,
ProfileMetricsModule,
LevelModule,
HealthModule,
AchievementsModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class CollectionsEntriesService {
) {
const isOwnQuery = userId === targetUserId;
const findOptions = buildBaseFindOptions(dto);

if (isOwnQuery) {
return await this.collectionEntriesRepository.findAndCount({
...findOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { Review } from "../../../reviews/entities/review.entity";

@Entity()
/**
* Represents an entry in a collection.
* Represents an entry in a collection. <br>
* Each CollectionEntry corresponds to a single game.
* @class CollectionEntry
*/
export class CollectionEntry {
Expand Down
2 changes: 1 addition & 1 deletion src/collections/collections.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export class CollectionsService {
}

/**
* Removes a collection and it's models.
* Removes a collection.
* @param userId
* @param collectionId
*/
Expand Down
3 changes: 1 addition & 2 deletions src/game/game-repository/entities/game.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
Expand Down Expand Up @@ -121,7 +120,7 @@ export class Game {
@ManyToMany(() => Game, (game) => game.expansionOf, {
nullable: true,
})
@JoinColumn()
@JoinTable()
expansions?: Game[];
@ManyToMany(() => Game, (game) => game.expansions, {
nullable: true,
Expand Down
2 changes: 1 addition & 1 deletion src/game/game-repository/game-repository-create.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ export class GameRepositoryCreateService {
if (companies == undefined || companies.length === 0) return;
for (const originalCompany of companies) {
// Avoids errors where company.id is transformed to "undefined".
// Probably TypeORM messing something interanally.
// Probably TypeORM messing something internally.
const company = structuredClone(originalCompany);
if (company == undefined || typeof company.id !== "number")
continue;
Expand Down
4 changes: 2 additions & 2 deletions src/game/game-repository/game-repository.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ export class GameRepositoryService {
return game;
}

private reOrderByIds(originalIds: number[], games: Game[]) {
const gamesMap = games.reduce((acc, current) => {
private reOrderByIds(originalIds: number[], unOrderedGames: Game[]) {
const gamesMap = unOrderedGames.reduce((acc, current) => {
acc.set(current.id, current);
return acc;
}, new Map<number, Game>());
Expand Down
3 changes: 1 addition & 2 deletions src/health/health.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { ApiTags } from "@nestjs/swagger";
@Controller("health")
@ApiTags("health")
export class HealthController {
constructor(private healthService: HealthService) {
}
constructor(private healthService: HealthService) {}

@Get()
async health() {
Expand Down
1 change: 0 additions & 1 deletion src/profile/dto/create-profile.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export class CreateProfileDto {
@Length(4, 20)
@IsNotEmpty()
username: string;
avatar: any;
@IsString()
@Length(1, 240)
@IsOptional()
Expand Down
14 changes: 14 additions & 0 deletions src/profile/dto/update-profile-image.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IsIn, IsNotEmpty } from "class-validator";
import { ApiProperty } from "@nestjs/swagger";

export type ProfileImageIdentifier = "avatar" | "banner";

export class UpdateProfileImageDto {
file: any;
@ApiProperty({
type: "string",
})
@IsNotEmpty()
@IsIn(["avatar", "banner"])
type: ProfileImageIdentifier;
}
14 changes: 2 additions & 12 deletions src/profile/entities/profile-avatar.entity.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
import {
Column,
CreateDateColumn,
Entity,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from "typeorm";
import { Profile } from "./profile.entity";
import { PersistedImageDetails } from "../../utils/persisted-image-details.entity";

@Entity()
export class ProfileAvatar {
export class ProfileAvatar extends PersistedImageDetails {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: false })
mimetype: string;
@Column({ nullable: false })
extension: string;
@Column({ nullable: false })
size: number;
@Column({ nullable: false })
filename: string;
@Column({ nullable: false })
encoding: string;
@OneToOne(() => Profile, (profile) => profile.avatar)
profile: Profile;
@CreateDateColumn()
Expand Down
21 changes: 21 additions & 0 deletions src/profile/entities/profile-banner.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
CreateDateColumn,
Entity,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from "typeorm";
import { PersistedImageDetails } from "../../utils/persisted-image-details.entity";
import { Profile } from "./profile.entity";

@Entity()
export class ProfileBanner extends PersistedImageDetails {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => Profile, (profile) => profile.avatar)
profile: Profile;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
13 changes: 12 additions & 1 deletion src/profile/entities/profile.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
} from "typeorm";
import { ProfileAvatar } from "./profile-avatar.entity";
import { UserFollow } from "../../follow/entity/user-follow.entity";
import { ProfileBanner } from "./profile-banner.entity";

/**
* Profiles represent a user in the community scope. Unlike 'Library', they should be used by tables representing
* interactions with the GameNode community (e.g. a user like). <br>
* Library's, Profile's and Supertokens' userIds are interchangeable.
*/
@Entity()
export class Profile {
/**
Expand All @@ -36,7 +42,12 @@ export class Profile {
nullable: true,
})
@JoinColumn()
avatar: ProfileAvatar;
avatar: ProfileAvatar | null;
@OneToOne(() => ProfileBanner, (banner) => banner.profile, {
nullable: true,
})
@JoinColumn()
banner: ProfileBanner | null;

@Column({
type: "timestamp",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { IsEnum, IsNotEmpty } from "class-validator";

export enum ProfileMetricsDistribution {
RELEASE_YEAR = "release_year",
FINISH_YEAR = "finish_year",
}

export class ProfileMetricsDistributionRequestDto {
@IsNotEmpty()
@IsEnum(ProfileMetricsDistribution)
by: ProfileMetricsDistribution;
}

/**
* Relation between a year (presented as string) and data for said period.
*/
export interface ProfileMetricsDistributionYearToData {
[p: string]: {
count: number;
totalEstimatedPlaytime: number;
};
}

export class ProfileMetricsDistributionResponseDto {
distribution: ProfileMetricsDistributionYearToData;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class ProfileMetricsOverviewDto {
totalGames: number;
totalCollections: number;
totalFinishedGames: number;
/**
* Total playtime spent on finished games, based on available data and HLTB's 'main' profile.
*/
totalEstimatedPlaytime: number;
}
43 changes: 43 additions & 0 deletions src/profile/profile-statistics/profile-metrics.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Controller, Get, Param, Query, UseGuards } from "@nestjs/common";
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
import { ProfileMetricsService } from "./profile-metrics.service";
import { ProfileMetricsOverviewDto } from "./dto/profile-metrics-overview.dto";
import { AuthGuard } from "../../auth/auth.guard";
import { Public } from "../../auth/public.decorator";
import {
ProfileMetricsDistributionRequestDto,
ProfileMetricsDistributionResponseDto,
} from "./dto/profile-metrics-distribution.dto";

@Controller("profile/metrics")
@ApiTags("profile-metrics")
@UseGuards(AuthGuard)
export class ProfileMetricsController {
constructor(
private readonly profileStatisticsService: ProfileMetricsService,
) {}

/**
* Retrieves basic stats for a user profile
* @see
*/
@Get("overview/:userId")
@ApiOkResponse({
type: ProfileMetricsOverviewDto,
})
@Public()
async getStatsOverview(@Param("userId") userId: string) {
return this.profileStatisticsService.getStatsOverview(userId);
}

@Get("distribution/:userId")
@ApiOkResponse({
type: ProfileMetricsDistributionResponseDto,
})
async getStatsDistribution(
@Param("userId") userId: string,
@Query() dto: ProfileMetricsDistributionRequestDto,
) {
return this.profileStatisticsService.getDistribution(userId, dto);
}
}
19 changes: 19 additions & 0 deletions src/profile/profile-statistics/profile-metrics.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Module } from "@nestjs/common";
import { ProfileMetricsController } from "./profile-metrics.controller";
import { ProfileMetricsService } from "./profile-metrics.service";
import { CollectionsEntriesModule } from "../../collections/collections-entries/collections-entries.module";
import { CollectionsModule } from "../../collections/collections.module";
import { HltbSyncModule } from "../../sync/hltb/hltb-sync.module";
import { GameRepositoryModule } from "../../game/game-repository/game-repository.module";

@Module({
imports: [
CollectionsModule,
CollectionsEntriesModule,
HltbSyncModule,
GameRepositoryModule,
],
controllers: [ProfileMetricsController],
providers: [ProfileMetricsService],
})
export class ProfileMetricsModule {}
Loading

0 comments on commit 21ccf53

Please sign in to comment.