nise/nise-frontend/src/app/view-user-score/view-user-score.component.ts

173 lines
5.7 KiB
TypeScript
Raw Normal View History

2024-03-04 23:32:57 +00:00
import {Component, OnInit, ViewChild} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {ActivatedRoute, RouterLink} from "@angular/router";
import {Title} from "@angular/platform-browser";
import {DistributionEntry, UserReplayData} from "../replays";
import {environment} from "../../environments/environment";
import {catchError, throwError} from "rxjs";
import {ChartComponent} from "../../corelib/components/chart/chart.component";
import {BaseChartDirective, NgChartsModule} from "ng2-charts";
import {DecimalPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common";
import {calculateAccuracy} from "../format";
import {ChartConfiguration} from "chart.js";
@Component({
selector: 'app-view-user-score',
standalone: true,
imports: [
ChartComponent,
NgChartsModule,
NgIf,
NgForOf,
RouterLink,
DecimalPipe,
NgOptimizedImage
],
templateUrl: './view-user-score.component.html',
styleUrl: './view-user-score.component.css'
})
export class ViewUserScoreComponent implements OnInit {
@ViewChild(BaseChartDirective)
public chart!: BaseChartDirective;
isLoading = false;
error: string | null = null;
replayData: UserReplayData | null = null;
replayId: number | null = null;
public barChartLegend = true;
public barChartPlugins = [];
public barChartData: ChartConfiguration<'bar'>['data'] = {
labels: [],
datasets: [
{ data: [], label: 'Miss (%)', backgroundColor: 'rgba(255,0,0,0.66)', borderRadius: 5 },
{ data: [], label: '50 (%)', backgroundColor: 'rgba(187,129,33,0.66)', borderRadius: 5 },
{ data: [], label: '100 (%)', backgroundColor: 'rgba(219,255,0,0.8)', borderRadius: 5 },
{ data: [], label: '300 (%)', backgroundColor: 'rgba(0,255,41,0.66)', borderRadius: 5 }
],
};
public barChartOptions: ChartConfiguration<'bar'>['options'] = {
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
};
constructor(private http: HttpClient,
private activatedRoute: ActivatedRoute,
private title: Title
) {}
ngOnInit(): void {
this.activatedRoute.params.subscribe(params => {
this.replayId = params['replayId'];
if (this.replayId) {
this.loadScoreData();
}
});
}
private loadScoreData(): void {
this.isLoading = true;
this.http.get<UserReplayData>(`${environment.apiUrl}/user-score/${this.replayId}`).pipe(
catchError(error => {
this.isLoading = false;
return throwError(() => new Error('An error occurred with the request: ' + error.message));
})
).subscribe({
next: (response) => {
this.isLoading = false;
this.replayData = response;
this.title.setTitle(
`${this.replayData.username} on ${this.replayData.beatmap_title} (${this.replayData.beatmap_version})`
)
let errorDistribution = Object.entries(this.replayData.error_distribution);
if (errorDistribution.length >= 1) {
const sortedEntries = errorDistribution
.sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
const chartData = this.generateChartData(sortedEntries);
this.barChartData.labels = chartData.labels;
for (let i = 0; i < 4; i++) {
this.barChartData.datasets[i].data = chartData.datasets[i];
}
}
},
error: (error) => {
this.error = error;
}
});
}
private getChartRange(entries: [string, DistributionEntry][]): [number, number] {
const keys = entries.map(([key, _]) => parseInt(key));
const minKey = Math.min(...keys);
const maxKey = Math.max(...keys);
return [minKey, maxKey];
}
private generateChartData(entries: [string, DistributionEntry][]): { labels: string[], datasets: number[][] } {
const range = this.getChartRange(entries);
const labels: string[] = [];
const datasets: number[][] = Array(4).fill(0).map(() => []);
const defaultPercentageValues: DistributionEntry = {
percentageMiss: 0,
percentage50: 0,
percentage100: 0,
percentage300: 0,
};
const entriesMap = new Map<number, DistributionEntry>(entries.map(([key, value]) => [parseInt(key), value]));
for (let key = range[0]; key <= range[1]; key += 2) {
const endKey = key + 2 <= range[1] ? key + 2 : key + 1;
labels.push(`${key}ms to ${endKey}ms`);
const currentEntry = entriesMap.get(key) || { ...defaultPercentageValues };
const nextEntry = key + 1 <= range[1] ? (entriesMap.get(key + 1) || { ...defaultPercentageValues }) : defaultPercentageValues;
const sumEntry: DistributionEntry = {
percentageMiss: currentEntry.percentageMiss + nextEntry.percentageMiss,
percentage50: currentEntry.percentage50 + nextEntry.percentage50,
percentage100: currentEntry.percentage100 + nextEntry.percentage100,
percentage300: currentEntry.percentage300 + nextEntry.percentage300,
};
datasets[0].push(sumEntry.percentageMiss);
datasets[1].push(sumEntry.percentage50);
datasets[2].push(sumEntry.percentage100);
datasets[3].push(sumEntry.percentage300);
}
// Handling the case for an odd last key if needed
if (range[1] % 2 !== range[0] % 2) {
const lastEntry = entriesMap.get(range[1]) || { ...defaultPercentageValues };
labels.push(`${range[1]}ms to ${range[1] + 1}ms`);
datasets[0].push(lastEntry.percentageMiss);
datasets[1].push(lastEntry.percentage50);
datasets[2].push(lastEntry.percentage100);
datasets[3].push(lastEntry.percentage300);
}
return { labels, datasets };
}
protected readonly Object = Object;
protected readonly calculateAccuracy = calculateAccuracy;
}