diff --git a/src/controllers/StatsController.ts b/src/controllers/StatsController.ts index 75e676b..03f5c51 100644 --- a/src/controllers/StatsController.ts +++ b/src/controllers/StatsController.ts @@ -9,6 +9,7 @@ import { RunnerTeam } from '../models/entities/RunnerTeam'; import { Scan } from '../models/entities/Scan'; import { User } from '../models/entities/User'; import { ResponseStats } from '../models/responses/ResponseStats'; +import { ResponseStatsOrgnisation } from '../models/responses/ResponseStatsOrganisation'; import { ResponseStatsRunner } from '../models/responses/ResponseStatsRunner'; import { ResponseStatsTeam } from '../models/responses/ResponseStatsTeam'; @@ -57,6 +58,14 @@ export class StatsController { return responseRunners; } + @Get("/scans") + @UseBefore(StatsAuth) + @ResponseSchema(ResponseStatsRunner, { isArray: true }) + @OpenAPI({ description: "Returns the top ten fastest track times (with their runner and the runner's group).", security: [{ "StatsApiToken": [] }] }) + async getTopRunnersByTrackTime() { + throw new Error("Not implemented yet.") + } + @Get("/teams/distance") @UseBefore(StatsAuth) @ResponseSchema(ResponseStatsTeam, { isArray: true }) @@ -89,13 +98,25 @@ export class StatsController { @UseBefore(StatsAuth) @OpenAPI({ description: "Returns the top ten organisations by distance.", security: [{ "StatsApiToken": [] }] }) async getTopOrgsByDistance() { - throw new Error("Not implemented yet.") + let orgs = await getConnection().getRepository(RunnerOrganisation).find({ relations: ['runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.scans', 'teams.runners.distanceDonations', 'teams.runners.scans.track'] }); + let topOrgs = orgs.sort((org1, org2) => org1.distance - org2.distance).slice(0, 9); + let responseOrgs: ResponseStatsOrgnisation[] = new Array(); + topOrgs.forEach(org => { + responseOrgs.push(new ResponseStatsOrgnisation(org)); + }); + return responseOrgs; } @Get("/organisations/donations") @UseBefore(StatsAuth) @OpenAPI({ description: "Returns the top ten organisations by donations.", security: [{ "StatsApiToken": [] }] }) async getTopOrgsByDonations() { - throw new Error("Not implemented yet.") + let orgs = await getConnection().getRepository(RunnerOrganisation).find({ relations: ['runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.scans', 'teams.runners.distanceDonations', 'teams.runners.scans.track'] }); + let topOrgs = orgs.sort((org1, org2) => org1.distanceDonationAmount - org2.distanceDonationAmount).slice(0, 9); + let responseOrgs: ResponseStatsOrgnisation[] = new Array(); + topOrgs.forEach(org => { + responseOrgs.push(new ResponseStatsOrgnisation(org)); + }); + return responseOrgs; } } \ No newline at end of file diff --git a/src/models/entities/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts index 8d24077..877784b 100644 --- a/src/models/entities/RunnerOrganisation.ts +++ b/src/models/entities/RunnerOrganisation.ts @@ -1,6 +1,7 @@ -import { IsOptional } from "class-validator"; +import { IsInt, IsOptional } from "class-validator"; import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; import { Address } from "./Address"; +import { Runner } from './Runner'; import { RunnerGroup } from "./RunnerGroup"; import { RunnerTeam } from "./RunnerTeam"; @@ -24,4 +25,32 @@ export class RunnerOrganisation extends RunnerGroup { */ @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) teams: RunnerTeam[]; + + /** + * Returns all runners associated with this organisation (directly or indirectly via teams). + */ + public get allRunners(): Runner[] { + let returnRunners: Runner[] = new Array(); + returnRunners.push(...this.runners); + for (let team of this.teams) { + returnRunners.push(...team.runners) + } + return returnRunners; + } + + /** + * Returns the total distance ran by this group's runners based on all their valid scans. + */ + @IsInt() + public get distance(): number { + return this.allRunners.reduce((sum, current) => sum + current.distance, 0); + } + + /** + * Returns the total donations a runner has collected based on his linked donations and distance ran. + */ + @IsInt() + public get distanceDonationAmount(): number { + return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0); + } } \ No newline at end of file diff --git a/src/models/responses/ResponseStatsOrganisation.ts b/src/models/responses/ResponseStatsOrganisation.ts new file mode 100644 index 0000000..2fa6ae4 --- /dev/null +++ b/src/models/responses/ResponseStatsOrganisation.ts @@ -0,0 +1,47 @@ +import { + IsInt, + + IsString +} from "class-validator"; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; + +/** + * Defines the org stats response. + * This differs from the normal org responce. +*/ +export class ResponseStatsOrgnisation { + /** + * The orgs's id. + */ + @IsInt() + id: number; + + /** + * The orgs's name. + */ + @IsString() + name: string; + + /** + * The orgs's runner's currently ran distance in meters. + */ + @IsInt() + distance: number; + + /** + * The orgs's currently collected donations. + */ + @IsInt() + donationAmount: number; + + /** + * Creates a ResponseRunner object from a runner. + * @param runner The user the response shall be build for. + */ + public constructor(org: RunnerOrganisation) { + this.name = org.name; + this.id = org.id; + this.distance = org.distance; + this.donationAmount = org.distanceDonationAmount; + } +}