From 2d53a13d824527118a4f9c6fb60ae6560d7749ea Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 24 Nov 2023 15:38:46 +0000 Subject: [PATCH] Inital pass at standings, countdown timer --- .../season-standings/season-standing.model.ts | 4 +- .../season-standings.service.ts | 47 +++++++++- .../src/seasons/seasons.service.ts | 3 +- .../src/upload/upload.controller.ts | 2 +- .../src/upload/upload.service.ts | 30 ------ .../race-details/race-details.component.html | 33 ++++++- .../race-details/race-details.component.scss | 53 ++++++++++- .../race-details/race-details.component.ts | 91 ++++++++++++++----- .../season-standings.component.html | 2 +- .../season-standings.component.ts | 22 +++-- .../upload-replay-dialog.component.ts | 4 +- .../src/app/models/season-standing.model.ts | 6 ++ .../bridge-ui/src/app/models/season.model.ts | 3 +- .../season-details.component.html | 2 +- .../app/services/server-endpoint.service.ts | 2 +- 15 files changed, 227 insertions(+), 77 deletions(-) create mode 100644 packages/bridge-ui/src/app/models/season-standing.model.ts diff --git a/packages/bridge-server/src/season-standings/season-standing.model.ts b/packages/bridge-server/src/season-standings/season-standing.model.ts index a5c82ef..dd966f1 100644 --- a/packages/bridge-server/src/season-standings/season-standing.model.ts +++ b/packages/bridge-server/src/season-standings/season-standing.model.ts @@ -1,10 +1,11 @@ -import { Column, Model, Table, HasMany, ForeignKey, BelongsTo } from "sequelize-typescript"; +import { Column, Model, Table, HasMany, ForeignKey, BelongsTo, PrimaryKey } from "sequelize-typescript"; import { Racer } from "src/racers/racer.model"; import { Season } from "src/seasons/season.model"; @Table export class SeasonStanding extends Model { @ForeignKey(() => Season) + @PrimaryKey @Column seasonId: number; @@ -12,6 +13,7 @@ export class SeasonStanding extends Model { season: Season @ForeignKey(() => Racer) + @PrimaryKey @Column racerId: number; diff --git a/packages/bridge-server/src/season-standings/season-standings.service.ts b/packages/bridge-server/src/season-standings/season-standings.service.ts index 814884b..eb944a0 100644 --- a/packages/bridge-server/src/season-standings/season-standings.service.ts +++ b/packages/bridge-server/src/season-standings/season-standings.service.ts @@ -4,6 +4,8 @@ import { SeasonStanding } from './season-standing.model'; import { Sequelize } from 'sequelize-typescript'; import { RaceResultsService } from 'src/race-results/race-results.service'; import { RacesService } from 'src/races/races.service'; +import { Racer } from 'src/racers/racer.model'; +import { RaceResult } from 'src/race-results/race-result.model'; @Injectable() export class SeasonStandingsService { @@ -16,6 +18,13 @@ export class SeasonStandingsService { ) {} + async findManyForSeason(seasonId) { + return this.seasonStandingModel.findAll({ + where: { seasonId : seasonId }, + include:[ Racer ] + }) + } + async findOrCreate(racerId: number, seasonId: number): Promise { try { return this.sequelize.transaction( async t => { @@ -35,14 +44,46 @@ export class SeasonStandingsService { } async updateStandings(seasonId: number) { + let racesInSeason = await this.racesService.findManyBySeason(seasonId); console.log(racesInSeason); - let seasonRacers = []; + let seasonRacers: Map = new Map(); + let seasonRaceResults: Map = new Map(); for(let race of racesInSeason) { - for( let result of race.results ){ -// if ( ) + let fastest = await this.raceResultsService.getFastestTimesForRace(race.id); + seasonRaceResults.set(race.id, fastest); + for(let result of fastest ) { + if( seasonRacers.has(result.racerId) == false ) { + seasonRacers.set(result.racerId, result.racer); + } } } + let maxRacePoints = seasonRacers.size; + let seasonPoints = new Map(); + + for( let results of seasonRaceResults ) { + let availablePoints = maxRacePoints; + results[1].sort((a,b) => { + return a.time - b.time; + }); + + for( let result of results[1] ) { + if (seasonPoints.has(result.racerId) == false) { + seasonPoints.set(result.racerId, { points: 0 }); + } + let currentPoints = seasonPoints.get(result.racerId); + if( currentPoints == undefined ) { + continue; + } + currentPoints.points+=availablePoints; + availablePoints -=1; + } + } + + for( let entry of seasonPoints) { + console.log(entry); + this.seasonStandingModel.upsert({seasonId: seasonId, racerId: entry[0], points: entry[1].points}); + } } } diff --git a/packages/bridge-server/src/seasons/seasons.service.ts b/packages/bridge-server/src/seasons/seasons.service.ts index 53de4e6..f094c9a 100644 --- a/packages/bridge-server/src/seasons/seasons.service.ts +++ b/packages/bridge-server/src/seasons/seasons.service.ts @@ -4,6 +4,7 @@ import { Season } from './season.model'; import { SeasonStanding } from 'src/season-standings/season-standing.model'; import { Race } from 'src/races/race.model'; import { Sequelize } from 'sequelize-typescript'; +import { Racer } from 'src/racers/racer.model'; @Injectable() export class SeasonsService { @@ -18,7 +19,7 @@ export class SeasonsService { } async findOne(id: number) { - return this.seasonModel.findOne({ where: {id: id} , include:{ all: true } }) + return this.seasonModel.findOne({ where: {id: id} , include:[{model: SeasonStanding, include:[Racer]}, Race] }) } async create(title: string, subTitle: string, startingDate: Date) { diff --git a/packages/bridge-server/src/upload/upload.controller.ts b/packages/bridge-server/src/upload/upload.controller.ts index 39f76bc..b21b680 100644 --- a/packages/bridge-server/src/upload/upload.controller.ts +++ b/packages/bridge-server/src/upload/upload.controller.ts @@ -14,6 +14,6 @@ export class UploadController { @Post('replay') @UseInterceptors(FileInterceptor('file')) uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body: any) { - this.uploadService.replayUploaded(file, body); + return this.uploadService.replayUploaded(file, body); } } diff --git a/packages/bridge-server/src/upload/upload.service.ts b/packages/bridge-server/src/upload/upload.service.ts index b8055ee..9b82bba 100644 --- a/packages/bridge-server/src/upload/upload.service.ts +++ b/packages/bridge-server/src/upload/upload.service.ts @@ -48,36 +48,6 @@ export class UploadService { console.log(result); this.seasonStandingsService.updateStandings(body.seasonId); - /* - fs.readFile(file.path, async function(err, buffer) - { - console.log(err) - console.log(buffer) - let buff = new AdvancableBuffer(buffer); - let header = new gbxHeader(); - header.parse(buff); - if (header.is_vaild == false) - { - return; - } - console.log(header); - let replay = new gbxReplay(); - replay.parse(buff); - - let currentRacer = await this.racersService.findOrCreate(replay.gamerHandle); - console.log(currentRacer); - - let race = await this.racesService.findOne({season: body.seasonId, mapUID: replay.mapUID}) - console.log(race); - - let result = await this.raceResultsService.create({race: race, racer: currentRacer, timeInMilliseconds: replay.bestTime, replayPath: file.destination}); - console.log(result); - - this.seasonStandingsService.updateStandings(body.seasonId); - - //res.status(200); - }); - */ } } diff --git a/packages/bridge-ui/src/app/components/race-details/race-details.component.html b/packages/bridge-ui/src/app/components/race-details/race-details.component.html index f8bac15..8131882 100644 --- a/packages/bridge-ui/src/app/components/race-details/race-details.component.html +++ b/packages/bridge-ui/src/app/components/race-details/race-details.component.html @@ -5,10 +5,10 @@

{{race.mapName}}

-

Author Time: {{race.authorTime}}

-

Gold Time: {{race.goldTime}}

-

Silver Time: {{race.silverTime}}

-

Bronze Time: {{race.bronzeTime}}

+

Author Time: {{formatMilliseconds(race.authorTime)}}

+

Gold Time: {{formatMilliseconds(race.goldTime)}}

+

Silver Time: {{formatMilliseconds(race.silverTime)}}

+

Bronze Time: {{formatMilliseconds(race.bronzeTime)}}

- +
+
+ @if(openToUploads) { +

Entries Close on: {{currentTime}}

+
+
+

Days

+

Hours

+

Minutes

+

Seconds

+
+
+

+

+

+

+
+
+ } + @else { +

Closed for new entries

+ } + +

diff --git a/packages/bridge-ui/src/app/components/race-details/race-details.component.scss b/packages/bridge-ui/src/app/components/race-details/race-details.component.scss index 3c7abe0..9cb5318 100644 --- a/packages/bridge-ui/src/app/components/race-details/race-details.component.scss +++ b/packages/bridge-ui/src/app/components/race-details/race-details.component.scss @@ -1,6 +1,7 @@ .img-thumbnail { width: 200px; //float: right; + box-shadow: 2px 6px 9px 2px rgb(0 0 0 / 20%); } .race-details { @@ -22,4 +23,54 @@ .map-times { margin-left: 15px; -} \ No newline at end of file +} + +.count-down-timer { + box-shadow: 2px 6px 9px 2px rgb(0 0 0 / 20%); + text-align: center; + background-color: var(--mat-table-background-color); + max-width: 400px; + margin: 20px auto; + //color: #d9a74a; + border-radius: 6px; + padding: 10px; + font-family: sans-serif; + + > p { + margin: 5px 0 15px 0; + } + + .wrapper { + .description, + .times { + display: grid; + grid-template-columns: repeat(4, calc(25% - 8px)); + grid-column-gap: 10px; + } + + .description { + > p { + margin: 0; + font: normal 14px sans-serif; + } + } + + .times { + p { + letter-spacing: -5px; + position: relative; + margin: 0; + font: normal 40px courier, sans-serif; + ::ng-deep img { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 30px; + display: block; + height: 30px; + } + } + } + } + } \ No newline at end of file diff --git a/packages/bridge-ui/src/app/components/race-details/race-details.component.ts b/packages/bridge-ui/src/app/components/race-details/race-details.component.ts index ee6d13b..4d8f47e 100644 --- a/packages/bridge-ui/src/app/components/race-details/race-details.component.ts +++ b/packages/bridge-ui/src/app/components/race-details/race-details.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; @@ -7,7 +7,6 @@ import { MatIconModule } from '@angular/material/icon'; import { MatTableModule } from '@angular/material/table'; import { RacesService } from '../../services/races.service'; -import { RacersService } from '../../services/racers.service'; import { RaceResultService } from '../../services/race-result.service'; import { Race } from '../../models/race.model'; import { Racer } from '../../models/racer.model'; @@ -27,27 +26,66 @@ import { RaceEntry } from '../../models/raceEntry.model'; styleUrl: './race-details.component.scss' }) -export class RaceDetailsComponent { +export class RaceDetailsComponent implements AfterViewInit { @Input() race?: Race; - racers: Map = new Map(); - //raceResults: Map = new Map; raceResults: any; + blob: Blob = new Blob(); displayedColumns: string[] = ['position', 'name', 'runTime', 'ghost']; sortedResults: RaceEntry[] = []; + openToUploads: boolean = true; + + date: any; + now: any; + targetDate: Date = new Date(2024, 5, 11); + targetTime: number = this.targetDate.getTime(); + difference: number = 0; + months: Array = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + currentTime: any = `${ + this.months[this.targetDate.getMonth()] + } ${this.targetDate.getDate()}, ${this.targetDate.getFullYear()}`; + + @ViewChild('days') days?: ElementRef; + @ViewChild('hours') hours?: ElementRef; + @ViewChild('minutes') minutes?: ElementRef; + @ViewChild('seconds') seconds?: ElementRef; + constructor( - private racersService: RacersService, private raceResultService: RaceResultService, private racesService: RacesService, ) {} - + ngOnInit() { if( this.race == undefined) { console.log("No race in race-details") return; } + this.targetDate = new Date(this.race.endDate); + this.targetTime = this.targetDate.getTime(); + + if(this.targetTime < Date.now()){ + this.openToUploads = false; + } + + this.currentTime = `${ + this.months[this.targetDate.getMonth()] + } ${this.targetDate.getDate()}, ${this.targetDate.getFullYear()}`; + this.racesService.getRaceResults(this.race.id).subscribe(data => { console.log(data) this.raceResults = data; @@ -65,22 +103,33 @@ export class RaceDetailsComponent { }) } - getBestTimeForRacer(racer_id: string): RaceEntry | undefined { - let results = this.raceResults.get(racer_id) - if (results == undefined ){ - return undefined + ngAfterViewInit(): void { + if(this.openToUploads == false) { + return; } - - let bestTime: number = 0xFFFFFFFF; - let bestResult = undefined - for(let result of results){ - if(result.time < bestTime) { - bestTime = result.time - bestResult = result; + + setInterval(() => { + this.tickTock(); + this.difference = this.targetTime - this.now; + this.difference = this.difference / (1000 * 60 * 60 * 24); + + if(this.days && this.hours && this.minutes && this.seconds){ + !isNaN(this.days.nativeElement.innerText) + ? (this.days.nativeElement.innerText = Math.floor(this.difference)) + : (this.days.nativeElement.innerHTML = ``); } - } + }, 1000); + } - return bestResult + tickTock() { + this.date = new Date(); + this.now = this.date.getTime(); + if(this.days != undefined && this.hours != undefined && this.minutes != undefined && this.seconds != undefined) { + this.days.nativeElement.innerText = Math.floor(this.difference); + this.hours.nativeElement.innerText = 23 - this.date.getHours(); + this.minutes.nativeElement.innerText = 60 - this.date.getMinutes(); + this.seconds.nativeElement.innerText = 60 - this.date.getSeconds(); + } } formatMilliseconds(milliseconds: number) @@ -104,8 +153,6 @@ export class RaceDetailsComponent { return "" } - blob: Blob = new Blob(); - onClickDownloadGhost(raceResult: any) { console.log(raceResult) this.raceResultService.getGhost(raceResult.id).subscribe(data => { diff --git a/packages/bridge-ui/src/app/components/season-standings/season-standings.component.html b/packages/bridge-ui/src/app/components/season-standings/season-standings.component.html index 8e148f2..7205cda 100644 --- a/packages/bridge-ui/src/app/components/season-standings/season-standings.component.html +++ b/packages/bridge-ui/src/app/components/season-standings/season-standings.component.html @@ -8,7 +8,7 @@ - + diff --git a/packages/bridge-ui/src/app/components/season-standings/season-standings.component.ts b/packages/bridge-ui/src/app/components/season-standings/season-standings.component.ts index 874ec4e..814b256 100644 --- a/packages/bridge-ui/src/app/components/season-standings/season-standings.component.ts +++ b/packages/bridge-ui/src/app/components/season-standings/season-standings.component.ts @@ -10,12 +10,7 @@ import { Racer } from '../../models/racer.model'; import { RaceEntry } from '../../models/raceEntry.model'; import { Season } from '../../models/season.model'; import { RacesService } from '../../services/races.service'; - -class SeasonStanding { - id: string = ""; - points: number = 0; - constructor(_id: string) { this.id = _id;} -} +import { SeasonStanding } from '../../models/season-standing.model'; @Component({ selector: 'app-season-standings', @@ -56,6 +51,12 @@ export class SeasonStandingsComponent { return; } + this.sortedStandings = [...this.season.standings] + console.log("Season Standings - Begin") + console.log(this.season); + console.log(this.sortedStandings); + console.log("Season Standings - end") + return; /* for (let race_id of this.season.races) { @@ -92,9 +93,12 @@ export class SeasonStandingsComponent { }*/ } - getRacerName(racerId: string): string { - let racer = this.seasonRacers.get(racerId); + getRacerName(racer: any): string { if( racer != undefined) { + if( racer.name == undefined || racer.name == "" ) { + return racer.gameHandle; + } + return racer.name + " (" + racer.gameHandle + ")" } @@ -114,6 +118,7 @@ export class SeasonStandingsComponent { return bestResult } + /* calculateSeasonPoints() { if(this.races == undefined) { @@ -155,4 +160,5 @@ export class SeasonStandingsComponent { this.sortedStandings = [...this.sortedStandings] } + */ } diff --git a/packages/bridge-ui/src/app/components/upload-replay-dialog/upload-replay-dialog.component.ts b/packages/bridge-ui/src/app/components/upload-replay-dialog/upload-replay-dialog.component.ts index b12f447..0b265d4 100644 --- a/packages/bridge-ui/src/app/components/upload-replay-dialog/upload-replay-dialog.component.ts +++ b/packages/bridge-ui/src/app/components/upload-replay-dialog/upload-replay-dialog.component.ts @@ -65,6 +65,8 @@ export class UploadReplayDialogComponent { let local = this.seasonId; formData.append("seasonId", local); const upload$ = this.replaysService.uploadReplay(formData); - upload$.subscribe(); + upload$.subscribe(data => { + window.location.reload(); + }); } } diff --git a/packages/bridge-ui/src/app/models/season-standing.model.ts b/packages/bridge-ui/src/app/models/season-standing.model.ts new file mode 100644 index 0000000..90bc1f9 --- /dev/null +++ b/packages/bridge-ui/src/app/models/season-standing.model.ts @@ -0,0 +1,6 @@ +import { Racer } from "./racer.model"; + +export class SeasonStanding { + racer?: Racer; + points: number = 0; +} \ No newline at end of file diff --git a/packages/bridge-ui/src/app/models/season.model.ts b/packages/bridge-ui/src/app/models/season.model.ts index 1ce151b..a1c76f9 100644 --- a/packages/bridge-ui/src/app/models/season.model.ts +++ b/packages/bridge-ui/src/app/models/season.model.ts @@ -1,4 +1,5 @@ import { Race } from "./race.model"; +import { SeasonStanding } from "./season-standing.model"; export class Season { id: string = "0"; @@ -7,5 +8,5 @@ export class Season { startingDate: Date = new Date(Date.now()); races: Race[] = []; racers: string[] = []; - standings: string[] = []; + standings: SeasonStanding[] = []; } \ No newline at end of file diff --git a/packages/bridge-ui/src/app/pages/season-details/season-details.component.html b/packages/bridge-ui/src/app/pages/season-details/season-details.component.html index 44fa52c..f22eab6 100644 --- a/packages/bridge-ui/src/app/pages/season-details/season-details.component.html +++ b/packages/bridge-ui/src/app/pages/season-details/season-details.component.html @@ -10,7 +10,7 @@
} - +
diff --git a/packages/bridge-ui/src/app/services/server-endpoint.service.ts b/packages/bridge-ui/src/app/services/server-endpoint.service.ts index 10d99b2..6e1110d 100644 --- a/packages/bridge-ui/src/app/services/server-endpoint.service.ts +++ b/packages/bridge-ui/src/app/services/server-endpoint.service.ts @@ -10,7 +10,7 @@ export class ServerEndpointService { constructor() { - this.currentHost = "localhost"; + this.currentHost = "172.16.40.146"; this.currentPort = "3000"; }
Name {{getRacerName(element.id)}} {{getRacerName(element.racer)}}