diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/Models.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/Models.kt index 6856c8e..a3a4f71 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/Models.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/Models.kt @@ -112,64 +112,11 @@ data class ReplayPairViewerData( val judgements2: List ) -data class UserReplayData( - val replay_id: UUID, - val osu_replay_id: Long?, - val username: String, - val beatmap_id: Int, - val beatmap_beatmapset_id: Int, - val beatmap_artist: String, - val beatmap_title: String, - val beatmap_star_rating: Double, - val beatmap_creator: String, - val beatmap_version: String, - val score: Int, - val mods: List, - val ur: Double?, - val adjusted_ur: Double?, - val frametime: Int, - val snaps: Int, - val hits: Int, - - var mean_error: Double?, - var error_variance: Double?, - var error_standard_deviation: Double?, - var minimum_error: Double?, - var maximum_error: Double?, - var error_range: Double?, - var error_coefficient_of_variation: Double?, - var error_kurtosis: Double?, - var error_skewness: Double?, - - var comparable_samples: Int? = null, - var comparable_mean_error: Double? = null, - var comparable_error_variance: Double? = null, - var comparable_error_standard_deviation: Double? = null, - var comparable_minimum_error: Double? = null, - var comparable_maximum_error: Double? = null, - var comparable_error_range: Double? = null, - var comparable_error_coefficient_of_variation: Double? = null, - var comparable_error_kurtosis: Double? = null, - var comparable_error_skewness: Double? = null, - - val perfect: Boolean, - val max_combo: Int, - - val count_300: Int, - val count_100: Int, - val count_50: Int, - val count_miss: Int, - - val similar_scores: List, - val error_distribution: Map, - val charts: List -) - data class ReplayData( val replay_id: Long, - val user_id: Int, + val user_id: Int?, val username: String, - val date: String, + val date: String?, val beatmap_id: Int, val beatmap_beatmapset_id: Int, val beatmap_artist: String, @@ -189,10 +136,9 @@ data class ReplayData( val beatmap_count_spinners: Int?, val score: Int, val mods: List, - val rank: String, + val rank: String?, val ur: Double?, val adjusted_ur: Double?, - val average_ur: Double?, val frametime: Int, val snaps: Int, val hits: Int, @@ -217,8 +163,9 @@ data class ReplayData( var comparable_error_coefficient_of_variation: Double? = null, var comparable_error_kurtosis: Double? = null, var comparable_error_skewness: Double? = null, + var comparable_adjusted_ur: Double? = null, - val pp: Double, + val pp: Double?, val perfect: Boolean, val max_combo: Int, diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/ScoreController.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/ScoreController.kt index fd04344..07a6f6a 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/ScoreController.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/ScoreController.kt @@ -49,7 +49,7 @@ class ScoreController( // Sort replays by date (the first replay will always be the oldest) val replays = listOf(replay1Data, replay2Data) - .sortedBy { Format.parseStringToDate(it.date) } + .sortedBy { Format.parseStringToDate(it.date!!) } val replayPair = ReplayPair(replays, replayPairStatistics) diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/UserScoresController.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/UserScoresController.kt index bec508d..61deaca 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/UserScoresController.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/controller/UserScoresController.kt @@ -1,12 +1,12 @@ package com.nisemoe.nise.controller -import com.nisemoe.nise.UserReplayData +import com.nisemoe.nise.ReplayData import com.nisemoe.nise.database.UserScoreService import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RestController -import java.util.UUID +import java.util.* @RestController class UserScoresController( @@ -14,7 +14,7 @@ class UserScoresController( ) { @GetMapping("user-score/{replayId}") - fun getScoreDetails(@PathVariable replayId: UUID): ResponseEntity { + fun getScoreDetails(@PathVariable replayId: UUID): ResponseEntity { val replayData = this.userScoreService.getReplayData(replayId) ?: return ResponseEntity.notFound().build() diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/database/BeatmapService.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/database/BeatmapService.kt index deea7d8..6f266e3 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/database/BeatmapService.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/database/BeatmapService.kt @@ -44,23 +44,4 @@ class BeatmapService( } } - fun getAverageUR(beatmapId: Int, excludeReplayId: Long): Double? { - val condition = SCORES.BEATMAP_ID.eq(beatmapId) - .and(SCORES.UR.isNotNull) - .and(SCORES.REPLAY_ID.notEqual(excludeReplayId)) - - val totalScores = dslContext.fetchCount( - SCORES, condition - ) - - if(totalScores < 50) - return null - - return dslContext - .select(avg(SCORES.UR)) - .from(SCORES) - .where(condition) - .fetchOneInto(Double::class.java) - } - } \ No newline at end of file diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/database/ScoreService.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/database/ScoreService.kt index 7358f53..09b22d8 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/database/ScoreService.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/database/ScoreService.kt @@ -174,7 +174,6 @@ class ScoreService( .fetchOne() ?: return null val beatmapId = result.get(BEATMAPS.BEATMAP_ID, Int::class.java) - val averageUR = beatmapService.getAverageUR(beatmapId = beatmapId, excludeReplayId = replayId) val hitDistribution = this.getHitDistribution(scoreId = result.get(SCORES.ID, Int::class.java)) val charts = this.getCharts(result) @@ -207,7 +206,6 @@ class ScoreService( score = result.get(SCORES.SCORE, Int::class.java), mods = Mod.parseModCombination(result.get(SCORES.MODS, Int::class.java)), rank = result.get(SCORES.RANK, String::class.java), - average_ur = averageUR, snaps = result.get(SCORES.SNAPS, Int::class.java), hits = result.get(SCORES.EDGE_HITS, Int::class.java), perfect = result.get(SCORES.PERFECT, Boolean::class.java), @@ -482,7 +480,8 @@ class ScoreService( avg(SCORES.ERROR_RANGE).`as`("avg_error_range"), avg(SCORES.ERROR_COEFFICIENT_OF_VARIATION).`as`("avg_error_coefficient_of_variation"), avg(SCORES.ERROR_KURTOSIS).`as`("avg_error_kurtosis"), - avg(SCORES.ERROR_SKEWNESS).`as`("avg_error_skewness") + avg(SCORES.ERROR_SKEWNESS).`as`("avg_error_skewness"), + avg(SCORES.ADJUSTED_UR).`as`("avg_adjusted_ur") ) .from(SCORES) .where(SCORES.BEATMAP_ID.eq(replayData.beatmap_id)) @@ -500,6 +499,7 @@ class ScoreService( replayData.comparable_error_coefficient_of_variation = otherScores.get("avg_error_coefficient_of_variation", Double::class.java) replayData.comparable_error_kurtosis = otherScores.get("avg_error_kurtosis", Double::class.java) replayData.comparable_error_skewness = otherScores.get("avg_error_skewness", Double::class.java) + replayData.comparable_adjusted_ur = otherScores.get("avg_adjusted_ur", Double::class.java) } fun mapLegacyJudgement(judgementType: JudgementType): CircleguardService.JudgementType { diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/database/UserScoreService.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/database/UserScoreService.kt index 93d225e..42d9b87 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/database/UserScoreService.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/database/UserScoreService.kt @@ -1,13 +1,13 @@ package com.nisemoe.nise.database import com.nisemoe.generated.tables.references.* -import com.nisemoe.nise.* +import com.nisemoe.nise.DistributionEntry +import com.nisemoe.nise.Format +import com.nisemoe.nise.ReplayData +import com.nisemoe.nise.ReplayDataSimilarScore import com.nisemoe.nise.osu.Mod -import com.nisemoe.nise.service.AuthService import com.nisemoe.nise.service.CompressJudgements import org.jooq.DSLContext -import org.jooq.Record -import org.jooq.impl.DSL import org.springframework.stereotype.Service import java.time.LocalDateTime import java.util.* @@ -16,18 +16,11 @@ import kotlin.math.roundToInt @Service class UserScoreService( private val dslContext: DSLContext, - private val authService: AuthService, + private val scoreService: ScoreService, private val compressJudgements: CompressJudgements ) { - fun getCharts(db: Record): List { - if (!authService.isAdmin()) return emptyList() - - return listOf(USER_SCORES.SLIDEREND_RELEASE_TIMES to "slider end release times", USER_SCORES.KEYPRESSES_TIMES to "keypress times") - .mapNotNull { (scoreType, title) -> db.get(scoreType)?.let { ReplayDataChart(title, it.filterNotNull()) } } - } - - fun getReplayData(replayId: UUID): UserReplayData? { + fun getReplayData(replayId: UUID): ReplayData? { val result = dslContext.select( USER_SCORES.ID, USER_SCORES.PLAYER_NAME, @@ -38,6 +31,16 @@ class UserScoreService( BEATMAPS.STAR_RATING, BEATMAPS.CREATOR, BEATMAPS.VERSION, + BEATMAPS.TOTAL_LENGTH, + BEATMAPS.MAX_COMBO, + BEATMAPS.BPM, + BEATMAPS.ACCURACY, + BEATMAPS.AR, + BEATMAPS.CS, + BEATMAPS.DRAIN, + BEATMAPS.COUNT_CIRCLES, + BEATMAPS.COUNT_SPINNERS, + BEATMAPS.COUNT_SLIDERS, USER_SCORES.ONLINE_SCORE_ID, USER_SCORES.TOTAL_SCORE, USER_SCORES.FRAMETIME, @@ -72,11 +75,10 @@ class UserScoreService( val beatmapId = result.get(BEATMAPS.BEATMAP_ID, Int::class.java) val hitDistribution = this.getHitDistribution(result.get(USER_SCORES.JUDGEMENTS, ByteArray::class.java)) - val charts = this.getCharts(result) + val charts = this.scoreService.getCharts(result) - val replayData = UserReplayData( - replay_id = replayId, - osu_replay_id = result.get(USER_SCORES.ONLINE_SCORE_ID, Long::class.java), + val replayData = ReplayData( + replay_id = result.get(USER_SCORES.ONLINE_SCORE_ID, Long::class.java), username = result.get(USER_SCORES.PLAYER_NAME, String::class.java), beatmap_id = beatmapId, beatmap_beatmapset_id = result.get(BEATMAPS.BEATMAPSET_ID, Int::class.java), @@ -85,6 +87,16 @@ class UserScoreService( beatmap_star_rating = result.get(BEATMAPS.STAR_RATING, Double::class.java), beatmap_creator = result.get(BEATMAPS.CREATOR, String::class.java), beatmap_version = result.get(BEATMAPS.VERSION, String::class.java), + beatmap_bpm = result.get(BEATMAPS.BPM, Double::class.java), + beatmap_max_combo = result.get(BEATMAPS.MAX_COMBO, Int::class.java), + beatmap_total_length = result.get(BEATMAPS.TOTAL_LENGTH, Int::class.java), + beatmap_accuracy = result.get(BEATMAPS.ACCURACY, Double::class.java), + beatmap_ar = result.get(BEATMAPS.AR, Double::class.java), + beatmap_cs = result.get(BEATMAPS.CS, Double::class.java), + beatmap_drain = result.get(BEATMAPS.DRAIN, Double::class.java), + beatmap_count_circles = result.get(BEATMAPS.COUNT_CIRCLES, Int::class.java), + beatmap_count_spinners = result.get(BEATMAPS.COUNT_SPINNERS, Int::class.java), + beatmap_count_sliders = result.get(BEATMAPS.COUNT_SLIDERS, Int::class.java), frametime = result.get(USER_SCORES.FRAMETIME, Double::class.java).toInt(), ur = result.get(USER_SCORES.UR, Double::class.java), adjusted_ur = result.get(USER_SCORES.ADJUSTED_UR, Double::class.java), @@ -109,9 +121,13 @@ class UserScoreService( error_kurtosis = result.get(USER_SCORES.ERROR_KURTOSIS, Double::class.java), error_skewness = result.get(USER_SCORES.ERROR_SKEWNESS, Double::class.java), charts = charts, - similar_scores = this.getSimilarScores(replayId) + similar_scores = this.getSimilarScores(replayId), + date = null, + pp = null, + rank = null, + user_id = null ) - this.loadComparableReplayData(replayData) + this.scoreService.loadComparableReplayData(replayData) return replayData } @@ -148,46 +164,6 @@ class UserScoreService( } } - fun loadComparableReplayData(replayData: UserReplayData) { - // Total samples - val totalSamples = dslContext.fetchCount( - SCORES, SCORES.BEATMAP_ID.eq(replayData.beatmap_id) - ) - - if(totalSamples <= 0) { - return - } - - // We will select same beatmap_id and same mods - val otherScores = dslContext.select( - DSL.avg(SCORES.MEAN_ERROR).`as`("avg_mean_error"), - DSL.avg(SCORES.ERROR_VARIANCE).`as`("avg_error_variance"), - DSL.avg(SCORES.ERROR_STANDARD_DEVIATION).`as`("avg_error_standard_deviation"), - DSL.avg(SCORES.MINIMUM_ERROR).`as`("avg_minimum_error"), - DSL.avg(SCORES.MAXIMUM_ERROR).`as`("avg_maximum_error"), - DSL.avg(SCORES.ERROR_RANGE).`as`("avg_error_range"), - DSL.avg(SCORES.ERROR_COEFFICIENT_OF_VARIATION).`as`("avg_error_coefficient_of_variation"), - DSL.avg(SCORES.ERROR_KURTOSIS).`as`("avg_error_kurtosis"), - DSL.avg(SCORES.ERROR_SKEWNESS).`as`("avg_error_skewness") - ) - .from(SCORES) - .where(SCORES.BEATMAP_ID.eq(replayData.beatmap_id)) - .fetchOne() ?: return - - replayData.comparable_samples = totalSamples - - replayData.comparable_mean_error = otherScores.get("avg_mean_error", Double::class.java) - replayData.comparable_error_variance = otherScores.get("avg_error_variance", Double::class.java) - replayData.comparable_error_standard_deviation = otherScores.get("avg_error_standard_deviation", Double::class.java) - replayData.comparable_minimum_error = otherScores.get("avg_minimum_error", Double::class.java) - replayData.comparable_maximum_error = otherScores.get("avg_maximum_error", Double::class.java) - replayData.comparable_error_range = otherScores.get("avg_error_range", Double::class.java) - replayData.comparable_error_coefficient_of_variation = otherScores.get("avg_error_coefficient_of_variation", Double::class.java) - replayData.comparable_error_kurtosis = otherScores.get("avg_error_kurtosis", Double::class.java) - replayData.comparable_error_skewness = otherScores.get("avg_error_skewness", Double::class.java) - } - - fun getHitDistribution(compressedJudgements: ByteArray): Map { val judgements = compressJudgements.deserialize(compressedJudgements) diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/scheduler/SendScoresToDiscord.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/scheduler/SendScoresToDiscord.kt index d148d39..0be66e9 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/scheduler/SendScoresToDiscord.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/scheduler/SendScoresToDiscord.kt @@ -194,8 +194,8 @@ class SendScoresToDiscord( ) statisticsEmbed.addEmbed(name = "Played by", value = "[${replayData.username}](https://osu.ppy.sh/users/${replayData.user_id})") - statisticsEmbed.addEmbed(name = "Played at", value = replayData.date) - statisticsEmbed.addEmbed(name = "PP", value = replayData.pp.roundToInt().toString()) + statisticsEmbed.addEmbed(name = "Played at", value = replayData.date!!) + statisticsEmbed.addEmbed(name = "PP", value = replayData.pp?.roundToInt().toString()) if(replayData.mods.isNotEmpty()) statisticsEmbed.addEmbed(name = "Mods", value = replayData.mods.joinToString("")) statisticsEmbed.addEmbed(name = "Max Combo", value = "${replayData.max_combo}x") diff --git a/nise-frontend/src/app/app-routing.module.ts b/nise-frontend/src/app/app-routing.module.ts index 97e9521..cd47f12 100644 --- a/nise-frontend/src/app/app-routing.module.ts +++ b/nise-frontend/src/app/app-routing.module.ts @@ -7,7 +7,6 @@ import {ViewScoreComponent} from "./view-score/view-score.component"; import {ViewUserComponent} from "./view-user/view-user.component"; import {ViewReplayPairComponent} from "./view-replay-pair/view-replay-pair.component"; import {SearchComponent} from "./search/search.component"; -import {ViewUserScoreComponent} from "./view-user-score/view-user-score.component"; const routes: Routes = [ {path: 'sus/:f', component: ViewSuspiciousScoresComponent, title: '/sus/'}, @@ -18,10 +17,10 @@ const routes: Routes = [ {path: 'u/:userId', component: ViewUserComponent}, {path: 's/:replayId', component: ViewScoreComponent}, + {path: 'c/:userReplayId', component: ViewScoreComponent}, {path: 'search', component: SearchComponent}, - {path: 'p/:replay1Id/:replay2Id', component: ViewReplayPairComponent}, - {path: 'c/:replayId', component: ViewUserScoreComponent}, + {path: 'p/:replay1Id/:replay2Id', component: ViewReplayPairComponent}, {path: '**', component: HomeComponent, title: '/nise.moe/'}, ]; diff --git a/nise-frontend/src/app/format.ts b/nise-frontend/src/app/format.ts index 43dda4c..1c13b88 100644 --- a/nise-frontend/src/app/format.ts +++ b/nise-frontend/src/app/format.ts @@ -1,4 +1,4 @@ -import {ReplayData, UserReplayData} from "./replays"; +import {ReplayData} from "./replays"; export function formatDuration(seconds: number): string | null { if(!seconds) { @@ -31,7 +31,7 @@ export function countryCodeToFlag(countryCode: string): string { return String.fromCodePoint(...codePoints); } -export function calculateAccuracy(replayData: ReplayData | UserReplayData): number { +export function calculateAccuracy(replayData: ReplayData): number { if(!replayData) { return 0; } diff --git a/nise-frontend/src/app/replays.ts b/nise-frontend/src/app/replays.ts index 689876e..f44d104 100644 --- a/nise-frontend/src/app/replays.ts +++ b/nise-frontend/src/app/replays.ts @@ -13,59 +13,6 @@ export interface ReplayDataSimilarScore { correlation: number; } -export interface UserReplayData { - replay_id: string; - osu_replay_id?: string; - username: string; - beatmap_id: number; - beatmap_beatmapset_id: number; - beatmap_artist: string; - beatmap_title: string; - beatmap_star_rating: number; - beatmap_creator: string; - beatmap_version: string; - score: number; - mods: string[]; - ur?: number; - adjusted_ur?: number; - frametime: number; - snaps: number; - hits: number; - - mean_error?: number; - error_variance?: number; - error_standard_deviation?: number; - minimum_error?: number; - maximum_error?: number; - error_range?: number; - error_coefficient_of_variation?: number; - error_kurtosis?: number; - error_skewness?: number; - - perfect: boolean; - max_combo: number; - - count_300: number; - count_100: number; - count_50: number; - count_miss: number; - - comparable_samples?: number; - comparable_mean_error?: number, - comparable_error_variance?: number, - comparable_error_standard_deviation?: number, - comparable_minimum_error?: number, - comparable_maximum_error?: number, - comparable_error_range?: number, - comparable_error_coefficient_of_variation?: number, - comparable_error_kurtosis?: number, - comparable_error_skewness?: number, - - similar_scores: ReplayDataSimilarScore[]; - error_distribution: ErrorDistribution; - charts: ReplayDataChart[]; -} - export function getMockReplayData(): ReplayData { return { 'replay_id': 123, @@ -93,7 +40,6 @@ export function getMockReplayData(): ReplayData { 'mods': ['mods'], 'rank': 'rank', 'ur': 123, - 'average_ur': 123, 'adjusted_ur': 123, 'frametime': 123, 'snaps': 123, @@ -146,7 +92,6 @@ export interface ReplayData { mods: string[]; rank: string; ur: number; - average_ur: number | null; adjusted_ur?: number; frametime: number; snaps: number; @@ -176,6 +121,7 @@ export interface ReplayData { comparable_error_coefficient_of_variation?: number, comparable_error_kurtosis?: number, comparable_error_skewness?: number, + comparable_adjusted_ur?: number; count_300: number, count_100: number, diff --git a/nise-frontend/src/app/view-score/view-score.component.html b/nise-frontend/src/app/view-score/view-score.component.html index 03915c0..f6fe954 100644 --- a/nise-frontend/src/app/view-score/view-score.component.html +++ b/nise-frontend/src/app/view-score/view-score.component.html @@ -33,9 +33,11 @@
- Played by {{ this.replayData.username }} osu!web -
- Submitted on {{ this.replayData.date }} + Played by {{ this.replayData.username }} osu!web + +
+ Submitted on {{ this.replayData.date }} +
@@ -130,7 +132,7 @@ Max Combo {{ this.replayData.max_combo }}x perfect -
+
PP {{ this.replayData.pp | number: '1.0-0' }}
@@ -218,10 +220,6 @@

# nerd stats

-
-

Heads up!

-

The average cvUR for this beatmap is {{ this.replayData.average_ur | number: '1.0-2' }}

-
@@ -269,6 +267,11 @@ + + + + +
{{ this.replayData.error_skewness | number: '1.2-2'}} {{ this.replayData.comparable_error_skewness | number: '1.2-2' }}
Adjusted cvUR{{ this.replayData.adjusted_ur | number: '1.2-2'}}{{ this.replayData.comparable_adjusted_ur | number: '1.2-2' }}
@@ -276,7 +279,7 @@ - + diff --git a/nise-frontend/src/app/view-score/view-score.component.ts b/nise-frontend/src/app/view-score/view-score.component.ts index 3a1e0fa..951ffd5 100644 --- a/nise-frontend/src/app/view-score/view-score.component.ts +++ b/nise-frontend/src/app/view-score/view-score.component.ts @@ -1,12 +1,11 @@ import {Component, OnInit, ViewChild} from '@angular/core'; -import {ChartConfiguration} from 'chart.js'; import {BaseChartDirective, NgChartsModule} from 'ng2-charts'; import {HttpClient} from "@angular/common/http"; import {environment} from "../../environments/environment"; import {DecimalPipe, JsonPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common"; import {ActivatedRoute, RouterLink} from "@angular/router"; import {catchError, throwError} from "rxjs"; -import {DistributionEntry, ReplayData} from "../replays"; +import {ReplayData} from "../replays"; import {calculateAccuracy} from "../format"; import {Title} from "@angular/platform-browser"; import {OsuGradeComponent} from "../../corelib/components/osu-grade/osu-grade.component"; @@ -43,6 +42,9 @@ export class ViewScoreComponent implements OnInit { isLoading = false; error: string | null = null; replayData: ReplayData | null = null; + + isUserScore: boolean = false; + userReplayId: string | null = null; replayId: number | null = null; constructor(private http: HttpClient, @@ -50,15 +52,27 @@ export class ViewScoreComponent implements OnInit { private title: Title ) {} - hasReplay(): boolean { + hasErrorDistribution(): boolean { return !!this.replayData?.error_distribution && Object.keys(this.replayData.error_distribution).length > 0; } + hasReplay(): boolean { + if(this.isUserScore) { + return false; + } + return this.hasErrorDistribution(); + } + ngOnInit(): void { this.activatedRoute.params.subscribe(params => { this.replayId = params['replayId']; + this.userReplayId = params['userReplayId']; if (this.replayId) { - this.loadScoreData(); + this.loadScoreData(`${environment.apiUrl}/score/${this.replayId}`); + } + if(this.userReplayId) { + this.loadScoreData(`${environment.apiUrl}/user-score/${this.userReplayId}`); + this.isUserScore = true; } }); } @@ -87,9 +101,9 @@ export class ViewScoreComponent implements OnInit { return url; } - private loadScoreData(): void { + private loadScoreData(url: string): void { this.isLoading = true; - this.http.get(`${environment.apiUrl}/score/${this.replayId}`).pipe( + this.http.get(url).pipe( catchError(error => { this.isLoading = false; return throwError(() => new Error('An error occurred with the request: ' + error.message)); diff --git a/nise-frontend/src/app/view-user-score/view-user-score.component.css b/nise-frontend/src/app/view-user-score/view-user-score.component.css deleted file mode 100644 index 1905efc..0000000 --- a/nise-frontend/src/app/view-user-score/view-user-score.component.css +++ /dev/null @@ -1,21 +0,0 @@ -/* Flex container */ -.flex-container { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 20px; /* Adjust the gap between items as needed */ -} - -/* Flex items - default to full width to stack on smaller screens */ -.flex-container > div { - flex: 0 0 100%; - box-sizing: border-box; /* To include padding and border in the element's total width and height */ -} - -/* Responsive columns */ -@media (min-width: 768px) { /* Adjust the breakpoint as needed */ - .flex-container > div { - flex: 0 0 15%; - max-width: 20%; - } -} diff --git a/nise-frontend/src/app/view-user-score/view-user-score.component.html b/nise-frontend/src/app/view-user-score/view-user-score.component.html deleted file mode 100644 index 4239cb2..0000000 --- a/nise-frontend/src/app/view-user-score/view-user-score.component.html +++ /dev/null @@ -1,203 +0,0 @@ - -
-
- Loading, please wait... -
-
-
- -
-
- An error occured. Maybe try again in a bit? -
-
-
- -
-

- This replay is user-submitted. As such, it might have been edited. -

-

- Please take the displayed data with a grain of salt. -

-
-
-
- - -
- -
- - {{ mod }} - -
- -

- {{ this.replayData.score | number }} -

- -
-
-
    -
  • Played by: {{ this.replayData.username }}
  • -
  • Link to score: osu!web
  • -
-
-
-
    -
  • - Max combo: {{ this.replayData.max_combo }}x - perfect -
  • -
  • Accuracy: {{ calculateAccuracy(this.replayData) | number: '1.2-2' }}%
  • -
  • 300x: {{ this.replayData.count_300 }}
  • -
  • 100x: {{ this.replayData.count_100 }}
  • -
  • 50x: {{ this.replayData.count_50 }}
  • -
  • Misses: {{ this.replayData.count_miss }}
  • - -
-
-
- - -
- -
-

cvUR

-
{{ this.replayData.ur | number: '1.2-2' }}
-
-
-

Adj. cvUR

-
{{ this.replayData.adjusted_ur | number: '1.2-2' }}
-
-
-

Frametime

-
{{ this.replayData.frametime | number: '1.0-2' }}ms
-
-
-

Edge Hits

-
{{ this.replayData.hits }}
-
-
-

Snaps

-
{{ this.replayData.snaps }}
-
-
-
-
- -
-

# similar replays

- - - - - - - - - - - - - - - - - - -
Played byPPDateSimilarityCorrelation
- {{ score.username }} - - {{ score.pp | number: '1.2-2' }} - - {{ score.date }} - - {{ score.similarity | number: '1.2-3' }} - - {{ score.correlation | number: '1.2-4' }} - - details -
-
- -
-

# nerd stats

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- this replay - - - avg. (n={{ this.replayData.comparable_samples }}) - -
Mean error{{ this.replayData.mean_error | number: '1.2-2' }}{{ this.replayData.comparable_mean_error | number: '1.2-2' }}
Error variance{{ this.replayData.error_variance | number: '1.2-2'}}{{ this.replayData.comparable_error_variance | number: '1.2-2' }}
Error Std. deviation{{ this.replayData.error_standard_deviation | number: '1.2-2'}}{{ this.replayData.comparable_error_standard_deviation | number: '1.2-2' }}
Min/max error[{{ this.replayData.minimum_error | number: '1.0-0' }}, {{ this.replayData.maximum_error | number: '1.0-0' }}][{{ this.replayData.comparable_minimum_error | number: '1.0-0' }}, {{ this.replayData.comparable_maximum_error | number: '1.0-0' }}]
Coefficient of variation{{ this.replayData.error_coefficient_of_variation | number: '1.2-2'}}{{ this.replayData.comparable_error_coefficient_of_variation | number: '1.2-2' }}
Kurtosis{{ this.replayData.error_kurtosis | number: '1.2-2'}}{{ this.replayData.comparable_error_kurtosis | number: '1.2-2' }}
Error skewness{{ this.replayData.error_skewness | number: '1.2-2'}}{{ this.replayData.comparable_error_skewness | number: '1.2-2' }}
-
- - - - - -
-

# hit distribution

- - -
-
diff --git a/nise-frontend/src/app/view-user-score/view-user-score.component.ts b/nise-frontend/src/app/view-user-score/view-user-score.component.ts deleted file mode 100644 index e3203f9..0000000 --- a/nise-frontend/src/app/view-user-score/view-user-score.component.ts +++ /dev/null @@ -1,172 +0,0 @@ -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(`${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 = { - countMiss: 0, - count50: 0, - count100: 0, - count300: 0, - }; - - const entriesMap = new Map(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 = { - countMiss: currentEntry.countMiss + nextEntry.countMiss, - count50: currentEntry.count50 + nextEntry.count50, - count100: currentEntry.count100 + nextEntry.count100, - count300: currentEntry.count300 + nextEntry.count300, - }; - - datasets[0].push(sumEntry.countMiss); - datasets[1].push(sumEntry.count50); - datasets[2].push(sumEntry.count100); - datasets[3].push(sumEntry.count300); - } - - // 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.countMiss); - datasets[1].push(lastEntry.count50); - datasets[2].push(lastEntry.count100); - datasets[3].push(lastEntry.count300); - } - - return { labels, datasets }; - } - - protected readonly Object = Object; - protected readonly calculateAccuracy = calculateAccuracy; -}