Refactor of hit distribution chart to fix incorrect data representation and fix of crash when no slider end timings or keypress timings were present in the score
This commit is contained in:
parent
01bd4e4948
commit
4f49a375d0
@ -32,72 +32,74 @@ class ScoreService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCharts(db: Record): List<ReplayDataChart> {
|
fun getCharts(db: Record): List<ReplayDataChart> {
|
||||||
// We only return additional charts if the user is an admin.
|
if (!authService.isAdmin())
|
||||||
if (!authService.isAdmin()) {
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|
||||||
|
val charts = mutableListOf<ReplayDataChart>()
|
||||||
|
|
||||||
|
val sliderEndReleaseTimes = db.get(SCORES.SLIDEREND_RELEASE_TIMES)
|
||||||
|
if(sliderEndReleaseTimes != null) {
|
||||||
|
val sliderEndData = sliderEndReleaseTimes
|
||||||
|
.filterNotNull()
|
||||||
|
val sliderFrequencyData: List<Pair<Double, Double>> = sliderEndData
|
||||||
|
.groupingBy { it }
|
||||||
|
.eachCount()
|
||||||
|
.map { (value, count) -> Pair(value, count / sliderEndData.size.toDouble() * 100) }
|
||||||
|
|
||||||
|
val sliderEndTable = mutableListOf<Triple<String, String, String>>()
|
||||||
|
|
||||||
|
sliderEndTable.add(Triple(
|
||||||
|
"Median",
|
||||||
|
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_MEDIAN)),
|
||||||
|
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_MEDIAN_ADJUSTED))
|
||||||
|
))
|
||||||
|
sliderEndTable.add(Triple(
|
||||||
|
"Std. deviation",
|
||||||
|
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_STANDARD_DEVIATION)),
|
||||||
|
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_STANDARD_DEVIATION_ADJUSTED))
|
||||||
|
))
|
||||||
|
|
||||||
|
val sliderEndChart = ReplayDataChart(
|
||||||
|
title = "slider end release times",
|
||||||
|
tableSamples = 0,
|
||||||
|
table = sliderEndTable,
|
||||||
|
data = sliderFrequencyData
|
||||||
|
)
|
||||||
|
charts.add(sliderEndChart)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slider end chart
|
val keypressTimes = db.get(SCORES.KEYPRESSES_TIMES)
|
||||||
val sliderEndData = db.get(SCORES.SLIDEREND_RELEASE_TIMES)!!
|
if(keypressTimes != null) {
|
||||||
.filterNotNull()
|
val keypressData = keypressTimes
|
||||||
val sliderFrequencyData: List<Pair<Double, Double>> = sliderEndData
|
.filterNotNull()
|
||||||
.groupingBy { it }
|
val keypressFrequencyData: List<Pair<Double, Double>> = keypressData
|
||||||
.eachCount()
|
.groupingBy { it }
|
||||||
.map { (value, count) -> Pair(value, count / sliderEndData.size.toDouble() * 100) }
|
.eachCount()
|
||||||
|
.map { (value, count) -> Pair(value, count / keypressData.size.toDouble() * 100) }
|
||||||
|
|
||||||
// Slider end table
|
val keypressTable = mutableListOf<Triple<String, String, String>>()
|
||||||
val sliderEndTable = mutableListOf<Triple<String, String, String>>()
|
|
||||||
|
|
||||||
sliderEndTable.add(Triple(
|
keypressTable.add(Triple(
|
||||||
"Median",
|
"Median",
|
||||||
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_MEDIAN)),
|
String.format("%.2f", db.get(SCORES.KEYPRESSES_MEDIAN)),
|
||||||
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_MEDIAN_ADJUSTED))
|
String.format("%.2f", db.get(SCORES.KEYPRESSES_MEDIAN_ADJUSTED))
|
||||||
))
|
))
|
||||||
sliderEndTable.add(Triple(
|
keypressTable.add(Triple(
|
||||||
"Std. deviation",
|
"Std. deviation",
|
||||||
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_STANDARD_DEVIATION)),
|
String.format("%.2f", db.get(SCORES.KEYPRESSES_STANDARD_DEVIATION)),
|
||||||
String.format("%.2f", db.get(SCORES.SLIDEREND_RELEASE_STANDARD_DEVIATION_ADJUSTED))
|
String.format("%.2f", db.get(SCORES.KEYPRESSES_STANDARD_DEVIATION_ADJUSTED))
|
||||||
))
|
))
|
||||||
|
|
||||||
val sliderEndChart = ReplayDataChart(
|
val keypressChart = ReplayDataChart(
|
||||||
title = "slider end release times",
|
title = "keypress release times",
|
||||||
tableSamples = 0,
|
tableSamples = 0,
|
||||||
table = sliderEndTable,
|
table = keypressTable,
|
||||||
data = sliderFrequencyData
|
data = keypressFrequencyData
|
||||||
)
|
)
|
||||||
|
charts.add(keypressChart)
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------
|
return charts
|
||||||
|
|
||||||
// Slider end chart
|
|
||||||
val keypressData = db.get(SCORES.KEYPRESSES_TIMES)!!
|
|
||||||
.filterNotNull()
|
|
||||||
val keypressFrequencyData: List<Pair<Double, Double>> = keypressData
|
|
||||||
.groupingBy { it }
|
|
||||||
.eachCount()
|
|
||||||
.map { (value, count) -> Pair(value, count / keypressData.size.toDouble() * 100) }
|
|
||||||
|
|
||||||
// Slider end table
|
|
||||||
val keypressTable = mutableListOf<Triple<String, String, String>>()
|
|
||||||
|
|
||||||
keypressTable.add(Triple(
|
|
||||||
"Median",
|
|
||||||
String.format("%.2f", db.get(SCORES.KEYPRESSES_MEDIAN)),
|
|
||||||
String.format("%.2f", db.get(SCORES.KEYPRESSES_MEDIAN_ADJUSTED))
|
|
||||||
))
|
|
||||||
keypressTable.add(Triple(
|
|
||||||
"Std. deviation",
|
|
||||||
String.format("%.2f", db.get(SCORES.KEYPRESSES_STANDARD_DEVIATION)),
|
|
||||||
String.format("%.2f", db.get(SCORES.KEYPRESSES_STANDARD_DEVIATION_ADJUSTED))
|
|
||||||
))
|
|
||||||
|
|
||||||
val keypressChart = ReplayDataChart(
|
|
||||||
title = "keypress release times",
|
|
||||||
tableSamples = 0,
|
|
||||||
table = keypressTable,
|
|
||||||
data = keypressFrequencyData
|
|
||||||
)
|
|
||||||
|
|
||||||
return listOf(sliderEndChart, keypressChart)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getReplayData(replayId: Long): ReplayData? {
|
fun getReplayData(replayId: Long): ReplayData? {
|
||||||
|
|||||||
@ -155,28 +155,27 @@ export class ViewScoreComponent implements OnInit {
|
|||||||
|
|
||||||
private getChartRange(entries: [string, DistributionEntry][]): [number, number] {
|
private getChartRange(entries: [string, DistributionEntry][]): [number, number] {
|
||||||
const keys = entries.map(([key, _]) => parseInt(key));
|
const keys = entries.map(([key, _]) => parseInt(key));
|
||||||
|
|
||||||
const minKey = Math.min(...keys);
|
const minKey = Math.min(...keys);
|
||||||
const maxKey = Math.max(...keys);
|
const maxKey = Math.max(...keys);
|
||||||
|
|
||||||
const absoluteMax = Math.max(Math.abs(minKey), Math.abs(maxKey));
|
return [minKey, maxKey];
|
||||||
return [absoluteMax * -1, absoluteMax];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateLabelsFromEntries(entries: [string, DistributionEntry][]): string[] {
|
private generateLabelsFromEntries(entries: [string, DistributionEntry][]): string[] {
|
||||||
const range = this.getChartRange(entries);
|
const range = this.getChartRange(entries);
|
||||||
|
|
||||||
const entriesMap = new Map(entries.map(([key, value]) => [parseInt(key), value]));
|
const labelEntries = [];
|
||||||
|
for (let key = range[0]; key <= range[1]; key += 2) {
|
||||||
const filledEntries = [];
|
const endKey = key + 2;
|
||||||
for (let key = range[0]; key <= range[1]; key++) {
|
labelEntries.push(`${key}ms to ${endKey}ms`);
|
||||||
filledEntries.push([key.toString(), entriesMap.get(key) || 0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filledEntries.map(([key, _]) => {
|
if (range[1] % 2 !== range[0] % 2) {
|
||||||
const start = parseInt(String(key));
|
labelEntries.push(`${range[1]}ms to ${range[1] + 1}ms`);
|
||||||
const end = start + 2;
|
}
|
||||||
return `${start}ms to ${end}ms`;
|
|
||||||
});
|
return labelEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateChartDataFromEntries(entries: [string, DistributionEntry][], i: number): number[] {
|
private generateChartDataFromEntries(entries: [string, DistributionEntry][], i: number): number[] {
|
||||||
@ -191,21 +190,36 @@ export class ViewScoreComponent implements OnInit {
|
|||||||
|
|
||||||
const entriesMap = new Map<number, DistributionEntry>(entries.map(([key, value]) => [parseInt(key), value]));
|
const entriesMap = new Map<number, DistributionEntry>(entries.map(([key, value]) => [parseInt(key), value]));
|
||||||
|
|
||||||
const filledEntries: [number, DistributionEntry][] = [];
|
const chartData = [];
|
||||||
for (let key = range[0]; key <= range[1]; key++) {
|
for (let key = range[0]; key <= range[1]; key += 2) {
|
||||||
filledEntries.push([key, entriesMap.get(key) || { ...defaultPercentageValues }]);
|
const currentEntry = entriesMap.get(key) || { ...defaultPercentageValues };
|
||||||
|
const nextEntry = entriesMap.get(key + 1) || { ...defaultPercentageValues };
|
||||||
|
|
||||||
|
const sumEntry: DistributionEntry = {
|
||||||
|
percentageMiss: currentEntry.percentageMiss + nextEntry.percentageMiss,
|
||||||
|
percentage50: currentEntry.percentage50 + nextEntry.percentage50,
|
||||||
|
percentage100: currentEntry.percentage100 + nextEntry.percentage100,
|
||||||
|
percentage300: currentEntry.percentage300 + nextEntry.percentage300,
|
||||||
|
};
|
||||||
|
|
||||||
|
chartData.push(sumEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the case where the last key is not included because of an odd number of total keys
|
||||||
|
if (range[1] % 2 !== range[0] % 2) {
|
||||||
|
const lastEntry = entriesMap.get(range[1]) || { ...defaultPercentageValues };
|
||||||
|
chartData.push(lastEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
const propertyMap = ['percentageMiss', 'percentage50', 'percentage100', 'percentage300'];
|
const propertyMap = ['percentageMiss', 'percentage50', 'percentage100', 'percentage300'];
|
||||||
|
|
||||||
if (i >= 0 && i < propertyMap.length) {
|
if (i >= 0 && i < propertyMap.length) {
|
||||||
return filledEntries.map(([, value]) => value[propertyMap[i] as keyof DistributionEntry]);
|
return chartData.map(entry => entry[propertyMap[i] as keyof DistributionEntry]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly Object = Object;
|
protected readonly Object = Object;
|
||||||
|
|
||||||
protected readonly calculateAccuracy = calculateAccuracy;
|
protected readonly calculateAccuracy = calculateAccuracy;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user