From 945d5886ea6f67f9a9e6d0093dc4bf5368490823 Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Sun, 3 Mar 2024 01:25:45 -0600 Subject: [PATCH] feat: genre graph --- .../ani/dantotsu/profile/StatsFragment.kt | 131 ++++++++++++++++-- 1 file changed, 123 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt index f3007300..7e9791ae 100644 --- a/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt +++ b/app/src/main/java/ani/dantotsu/profile/StatsFragment.kt @@ -22,11 +22,14 @@ import com.github.aachartmodel.aainfographics.aachartcreator.AADataElement import com.github.aachartmodel.aainfographics.aachartcreator.AAOptions import com.github.aachartmodel.aainfographics.aachartcreator.AASeriesElement import com.github.aachartmodel.aainfographics.aachartcreator.aa_toAAOptions +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAArea +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAChart import com.github.aachartmodel.aainfographics.aaoptionsmodel.AADataLabels import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAItemStyle import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAMarker import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAPlotOptions import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAStyle +import com.github.aachartmodel.aainfographics.aaoptionsmodel.AAYAxis import com.github.aachartmodel.aainfographics.aatools.AAColor import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -163,6 +166,46 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P binding.releaseYearChartView.visibility = View.GONE } + val startYearChartModel = getStartYearChartModel(anime) + if (startYearChartModel != null) { + binding.startYearChartView.visibility = View.VISIBLE + val aaOptions = buildOptions(startYearChartModel, false, """ + function () { + return 'Year: ' + + this.x + + '
' + + ' ${getTypeName()} ' + + this.y + } + """.trimIndent() + ) + binding.startYearChartView.aa_drawChartWithChartOptions(aaOptions) + } else { + binding.startYearChartView.visibility = View.GONE + } + + val genreChartModel = getGenreChartModel(anime) + if (genreChartModel.first != null) { + binding.genreChartView.visibility = View.VISIBLE + val aaOptions = buildOptions(genreChartModel.first!!, true, """ + function () { + return 'Genre: ' + + this.x + + '
' + + ' ${getTypeName()} ' + + this.y + } + """.trimIndent() + ) + val min = genreChartModel.second.first + val max = genreChartModel.second.second + aaOptions.yAxis = AAYAxis().min(min).max(max).tickInterval(if (max > 100) 20 else 10) + binding.genreChartView.aa_drawChartWithChartOptions(aaOptions) + } else { + binding.genreChartView.visibility = View.GONE + } + + } private fun buildOptions( @@ -302,9 +345,9 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P private fun getLengthChartModel(anime: Boolean): AAChartModel? { val names: List = if (anime) { - stats?.data?.user?.statistics?.anime?.lengths?.mapNotNull { it.length } ?: emptyList() + stats?.data?.user?.statistics?.anime?.lengths?.map { it.length?: "unknown" } ?: emptyList() } else { - stats?.data?.user?.statistics?.manga?.lengths?.mapNotNull { it.length } ?: emptyList() + stats?.data?.user?.statistics?.manga?.lengths?.map { it.length?: "unknown" } ?: emptyList() } val values: List = if (anime) { when (statType) { @@ -351,7 +394,6 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.releaseYears?.map { it.meanScore } } ?: emptyList() } - //clear nulls from names if (names.isEmpty() || values.isEmpty()) return null val palette = generateColorPalette(primaryColor, names.size) @@ -368,6 +410,79 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P .colorsTheme(hexColorsArray) } + private fun getStartYearChartModel(anime: Boolean): AAChartModel? { + val names: List = if (anime) { + stats?.data?.user?.statistics?.anime?.startYears?.map { it.startYear } ?: emptyList() + } else { + stats?.data?.user?.statistics?.manga?.startYears?.map { it.startYear } ?: emptyList() + } + val values: List = if (anime) { + when (statType) { + StatType.COUNT -> stats?.data?.user?.statistics?.anime?.startYears?.map { it.count } + StatType.TIME -> stats?.data?.user?.statistics?.anime?.startYears?.map { it.minutesWatched / 60 } + StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.startYears?.map { it.meanScore } + } ?: emptyList() + } else { + when (statType) { + StatType.COUNT -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.count } + StatType.TIME -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.chaptersRead } + StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.startYears?.map { it.meanScore } + } ?: emptyList() + } + if (names.isEmpty() || values.isEmpty()) + return null + val palette = generateColorPalette(primaryColor, names.size) + val hexColorsArray: Array = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray() + return AAChartModel() + .chartType(AAChartType.Bar) + .title("Start Year") + .subtitle(getTypeName()) + .zoomType(AAChartZoomType.XY) + .dataLabelsEnabled(false) + .yAxisTitle(getTypeName()) + .stacking(AAChartStackingType.Normal) + .series(getElementsSimple(names, values)) + .colorsTheme(hexColorsArray) + } + + private fun getGenreChartModel(anime: Boolean): Pair> { + val names: List = if (anime) { + stats?.data?.user?.statistics?.anime?.genres?.map { it.genre } ?: emptyList() + } else { + stats?.data?.user?.statistics?.manga?.genres?.map { it.genre } ?: emptyList() + } + val values: List = if (anime) { + when (statType) { + StatType.COUNT -> stats?.data?.user?.statistics?.anime?.genres?.map { it.count } + StatType.TIME -> stats?.data?.user?.statistics?.anime?.genres?.map { it.minutesWatched / 60 } + StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.anime?.genres?.map { it.meanScore } + } ?: emptyList() + } else { + when (statType) { + StatType.COUNT -> stats?.data?.user?.statistics?.manga?.genres?.map { it.count } + StatType.TIME -> stats?.data?.user?.statistics?.manga?.genres?.map { it.chaptersRead } + StatType.MEAN_SCORE -> stats?.data?.user?.statistics?.manga?.genres?.map { it.meanScore } + } ?: emptyList() + } + if (names.isEmpty() || values.isEmpty()) + return Pair(null, Pair(0, 0)) + val palette = generateColorPalette(primaryColor, names.size) + val hexColorsArray: Array = palette.map { String.format("#%06X", 0xFFFFFF and it) }.toTypedArray() + return Pair(AAChartModel() + .chartType(AAChartType.Area) + .title("Genre") + .subtitle(getTypeName()) + .zoomType(AAChartZoomType.XY) + .dataLabelsEnabled(false) + .legendEnabled(false) + .yAxisTitle(getTypeName()) + .stacking(AAChartStackingType.Normal) + .series(getElementsSimple(names, values)) + .colorsTheme(hexColorsArray) + .categories(names.toTypedArray()), + Pair(values.minOf { it.toInt() }, values.maxOf { it.toInt() })) + } + enum class StatType { COUNT, TIME, MEAN_SCORE } @@ -409,6 +524,7 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P .backgroundColor(AAColor.rgbaColor(255, 255, 255, 0.0f)) ) } else { + element.x(i) element.name(names[i] as String) } statDataElements.add(element) @@ -420,17 +536,16 @@ class StatsFragment(private val user: Query.UserProfile, private val activity: P } private fun getElementsSimple( - names: List, - statData: List + names: List, + statData: List ): Array { - val statValues = mutableListOf>() + val statValues = mutableListOf>() for (i in statData.indices) { statValues.add(arrayOf(names[i], statData[i], statData[i])) } - val sorted = statValues.sortedBy { it[0] as Int } return arrayOf( AASeriesElement().name("Score") - .data(sorted.toTypedArray()) + .data(statValues.toTypedArray()) .dataLabels(AADataLabels() .enabled(false) )