|
|
|
|
@@ -24,7 +24,8 @@ object SessionShareUtils {
|
|
|
|
|
val uniqueProblemsCompleted: Int,
|
|
|
|
|
val averageGrade: String?,
|
|
|
|
|
val sessionDuration: String,
|
|
|
|
|
val topResult: AttemptResult?
|
|
|
|
|
val topResult: AttemptResult?,
|
|
|
|
|
val topGrade: String?
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
fun calculateSessionStats(
|
|
|
|
|
@@ -58,6 +59,19 @@ object SessionShareUtils {
|
|
|
|
|
else -> null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine highest achieved grade (only from completed problems: SUCCESS or FLASH)
|
|
|
|
|
val completedProblems = problems.filter { it.id in uniqueCompletedProblems }
|
|
|
|
|
val completedBoulder = completedProblems.filter { it.climbType == ClimbType.BOULDER }
|
|
|
|
|
val completedRope = completedProblems.filter { it.climbType == ClimbType.ROPE }
|
|
|
|
|
val topBoulder = highestGradeForProblems(completedBoulder)
|
|
|
|
|
val topRope = highestGradeForProblems(completedRope)
|
|
|
|
|
val topGrade = when {
|
|
|
|
|
topBoulder != null && topRope != null -> "$topBoulder / $topRope"
|
|
|
|
|
topBoulder != null -> topBoulder
|
|
|
|
|
topRope != null -> topRope
|
|
|
|
|
else -> null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val duration = if (session.duration != null) "${session.duration}m" else "Unknown"
|
|
|
|
|
val topResult = attempts.maxByOrNull {
|
|
|
|
|
when (it.result) {
|
|
|
|
|
@@ -76,7 +90,8 @@ object SessionShareUtils {
|
|
|
|
|
uniqueProblemsCompleted = uniqueCompletedProblems.size,
|
|
|
|
|
averageGrade = averageGrade,
|
|
|
|
|
sessionDuration = duration,
|
|
|
|
|
topResult = topResult
|
|
|
|
|
topResult = topResult,
|
|
|
|
|
topGrade = topGrade
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -249,8 +264,8 @@ object SessionShareUtils {
|
|
|
|
|
drawStatItem(canvas, width - columnWidth / 2f, rightY, "Completed", stats.uniqueProblemsCompleted.toString(), statLabelPaint, statValuePaint)
|
|
|
|
|
rightY += 140f
|
|
|
|
|
|
|
|
|
|
stats.averageGrade?.let { grade ->
|
|
|
|
|
drawStatItem(canvas, width - columnWidth / 2f, rightY, "Avg Grade", grade, statLabelPaint, statValuePaint)
|
|
|
|
|
stats.topGrade?.let { grade ->
|
|
|
|
|
drawStatItem(canvas, width - columnWidth / 2f, rightY, "Top Grade", grade, statLabelPaint, statValuePaint)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Success rate arc
|
|
|
|
|
@@ -383,4 +398,48 @@ object SessionShareUtils {
|
|
|
|
|
e.printStackTrace()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the highest grade string among the given problems, respecting their difficulty system.
|
|
|
|
|
*/
|
|
|
|
|
private fun highestGradeForProblems(problems: List<Problem>): String? {
|
|
|
|
|
if (problems.isEmpty()) return null
|
|
|
|
|
return problems.maxByOrNull { p -> gradeRank(p.difficulty.system, p.difficulty.grade) }?.difficulty?.grade
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Produces a comparable numeric rank for grades across supported systems.
|
|
|
|
|
*/
|
|
|
|
|
private fun gradeRank(system: DifficultySystem, grade: String): Double {
|
|
|
|
|
return when (system) {
|
|
|
|
|
DifficultySystem.V_SCALE -> {
|
|
|
|
|
if (grade == "VB") 0.0 else grade.removePrefix("V").toDoubleOrNull() ?: -1.0
|
|
|
|
|
}
|
|
|
|
|
DifficultySystem.FONT -> {
|
|
|
|
|
val list = DifficultySystem.FONT.getAvailableGrades()
|
|
|
|
|
val idx = list.indexOf(grade.uppercase())
|
|
|
|
|
if (idx >= 0) idx.toDouble() else grade.filter { it.isDigit() }.toDoubleOrNull() ?: -1.0
|
|
|
|
|
}
|
|
|
|
|
DifficultySystem.YDS -> {
|
|
|
|
|
// Parse 5.X with optional letter a-d
|
|
|
|
|
val s = grade.lowercase()
|
|
|
|
|
if (!s.startsWith("5.")) return -1.0
|
|
|
|
|
val tail = s.removePrefix("5.")
|
|
|
|
|
val numberPart = tail.takeWhile { it.isDigit() || it == '.' }
|
|
|
|
|
val letterPart = tail.drop(numberPart.length).firstOrNull()
|
|
|
|
|
val base = numberPart.toDoubleOrNull() ?: return -1.0
|
|
|
|
|
val letterWeight = when (letterPart) {
|
|
|
|
|
'a' -> 0.0
|
|
|
|
|
'b' -> 0.1
|
|
|
|
|
'c' -> 0.2
|
|
|
|
|
'd' -> 0.3
|
|
|
|
|
else -> 0.0
|
|
|
|
|
}
|
|
|
|
|
base + letterWeight
|
|
|
|
|
}
|
|
|
|
|
DifficultySystem.CUSTOM -> {
|
|
|
|
|
grade.filter { it.isDigit() || it == '.' || it == '-' }.toDoubleOrNull() ?: -1.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|