diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt index 4cd8c649..5e4de50f 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt @@ -177,7 +177,7 @@ class Calculator { results.computeStatVariations(comparedResults) } - if (options.shouldManageMultiGroupProgressValues == true) { + if (options.shouldManageMultiGroupProgressValues) { group.comparedComputedResults = report.results.lastOrNull() } @@ -268,15 +268,15 @@ class Calculator { // Iterate for each session if (shouldIterateOverComputables) { - var index: Int = 0 + var index = 0 var tSum = 0.0 var tBBSum = 0.0 var tBBSessionCount = 0 var tWinningSessionCount = 0 var tBuyinSum = 0.0 var tHands = 0.0 - var longestWinStreak = 0; - var longestLoseStreak = 0; + var longestWinStreak = 0 + var longestLoseStreak = 0 var currentStreak = 0 computables.forEach { computable -> @@ -316,6 +316,7 @@ class Calculator { data = session ) results.addEvolutionValue(tBuyinSum / index, stat = AVERAGE_BUYIN, data = session) + results.addEvolutionValue(computable.ratedNet, stat = STANDARD_DEVIATION, data = session) Stat.netBBPer100Hands(tBBSum, tHands)?.let { netBB100 -> results.addEvolutionValue(netBB100, stat = NET_BB_PER_100_HANDS, data = session) @@ -355,7 +356,7 @@ class Calculator { } } - val shouldIterateOverSets = computableGroup.conditions.size > 0 || + val shouldIterateOverSets = computableGroup.conditions.isNotEmpty() || options.evolutionValues != Options.EvolutionValues.NONE || options.computeDaysPlayed @@ -366,8 +367,8 @@ class Calculator { var tRatedNetSum = 0.0 var tBBSum = 0.0 var tTotalHands = 0.0 - var tHourlyRate = 0.0 - var tHourlyRateBB = 0.0 + var tHourlyRate: Double + var tHourlyRateBB: Double val daysSet = mutableSetOf() var tMaxDuration = 0.0 diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt index 49fa3454..ccb2441e 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt @@ -1,15 +1,17 @@ package net.pokeranalytics.android.calculus import android.content.Context -import com.github.mikephil.charting.data.BarEntry -import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.* import io.realm.Realm import io.realm.RealmResults +import net.pokeranalytics.android.R import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.interfaces.Timed import net.pokeranalytics.android.model.realm.ComputableResult import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.SessionSet +import net.pokeranalytics.android.ui.graph.PALineDataSet +import kotlin.math.abs /** * The class returned after performing calculation in the Calculator object @@ -34,7 +36,7 @@ class Report(var options: Calculator.Options) { * Returns the list of entries corresponding to the provided [stat] * One value will be returned by result */ - fun lineEntries(stat: Stat): List { + fun lineEntries(stat: Stat, context: Context): LineDataSet { val entries = mutableListOf() this._results.forEachIndexed { index, results -> @@ -42,11 +44,11 @@ class Report(var options: Calculator.Options) { entries.add(Entry(index.toFloat(), progressValue.toFloat(), results)) } } - return entries + return PALineDataSet(entries, stat.name, context) } - fun barEntries(stat: Stat? = null): List { - val entries = mutableListOf() + fun barEntries(stat: Stat? = null): BarDataSet { + val entries = mutableListOf() val statToUse = stat ?: options.displayedStats.firstOrNull() statToUse?.let { @@ -59,16 +61,16 @@ class Report(var options: Calculator.Options) { } } - return entries + return BarDataSet(entries, stat?.name) } - fun multiLineEntries(): List> { + fun multiLineEntries(context: Context): List> { val entries = mutableListOf>() options.displayedStats.forEach { stat -> this._results.forEach { result -> - val entryList = result.singleLineEntries(stat) - entries.add(entryList) + val dataSet = result.singleLineEntries(stat, context) +// entries.add(entryList) } } @@ -328,48 +330,39 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu this.consolidateProgressStats() - if (options.evolutionValues != Calculator.Options.EvolutionValues.NONE) { - - // Sort points as a distribution - this._computedStats.keys.filter { it.hasDistributionSorting() }.forEach { _ -> - // @todo sort -// var evolutionValues = this._evolutionValues[stat] -// evolutionValues.so - } - - } } // MPAndroidChart - fun defaultStatEntries(stat: Stat): List { + fun defaultStatEntries(stat: Stat, context: Context): DataSet { return when (stat) { Stat.NUMBER_OF_SETS, Stat.NUMBER_OF_GAMES -> this.barEntries(stat) - else -> this.singleLineEntries(stat) + Stat.STANDARD_DEVIATION -> this.distributionEntries(stat, context) + else -> this.singleLineEntries(stat, context) } } - fun singleLineEntries(stat: Stat): List { + fun singleLineEntries(stat: Stat, context: Context): LineDataSet { val entries = mutableListOf() this._evolutionValues[stat]?.let { points -> points.forEachIndexed { index, p -> entries.add(Entry(index.toFloat(), p.y.toFloat(), p.data)) } } - return entries + return PALineDataSet(entries, stat.name, context) } - fun durationEntries(stat: Stat): List { + fun durationEntries(stat: Stat, context: Context): LineDataSet { val entries = mutableListOf() this._evolutionValues[stat]?.let { points -> points.forEach { p -> entries.add(Entry(p.x.toFloat(), p.y.toFloat(), p.data)) } } - return entries + return PALineDataSet(entries, stat.name, context) } - fun barEntries(stat: Stat): List { + fun barEntries(stat: Stat): BarDataSet { val entries = mutableListOf() this._evolutionValues[stat]?.let { points -> @@ -377,7 +370,44 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu entries.add(BarEntry(p.x.toFloat(), p.y.toFloat(), p.data)) } } - return entries + val dataSet = BarDataSet(entries, stat.name) + dataSet.setDrawValues(false) + return dataSet + } + + fun distributionEntries(stat: Stat, context: Context): BarDataSet { + + val colors = mutableListOf() + val entries = mutableListOf() + this._evolutionValues[stat]?.let { points -> + + val negative = mutableListOf() + val positive = mutableListOf() + + points.sortByDescending { it.y } + points.forEach { + if (it.y < 0) { + negative.add(it) + } else { + positive.add(it) + } + } + + negative.forEachIndexed { index, p -> + entries.add(BarEntry(index.toFloat(), abs(p.y.toFloat()), p.data)) + colors.add(context.getColor(R.color.red)) + } + positive.forEachIndexed { index, p -> + val x = negative.size + index.toFloat() + entries.add(BarEntry(x, p.y.toFloat(), p.data)) + colors.add(context.getColor(R.color.green)) + } + + } + val dataSet = BarDataSet(entries, stat.name) + dataSet.colors = colors + dataSet.setDrawValues(false) + return dataSet } val isEmpty: Boolean diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt index c9f76c35..bacf6695 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -94,16 +94,6 @@ enum class Stat : RowRepresentable { TOTAL_BUYIN, ; - /** - * Returns whether the stat evolution numericValues requires a distribution sorting - */ - fun hasDistributionSorting(): Boolean { - return when (this) { - STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> true - else -> false - } - } - companion object { fun returnOnInvestment(netResult: Double, buyin: Double): Double? { @@ -224,14 +214,6 @@ enum class Stat : RowRepresentable { } } - val graphType: GraphType - get() { - return when (this) { - NUMBER_OF_SETS, NUMBER_OF_GAMES -> GraphType.BAR - else -> GraphType.LINE - } - } - val aggregationTypes: List get() { return when (this) { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarDetailsFragment.kt index 1741e0eb..7d277cfc 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarDetailsFragment.kt @@ -187,10 +187,10 @@ class CalendarDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.NET_RESULT), it.computedStat(Stat.HOURLY_RATE))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.LOCATIONS_PLAYED), it.computedStat(Stat.LONGEST_STREAKS))) rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.distribution)) - rowRepresentables.add(GraphRow(report, Stat.NET_RESULT)) + rowRepresentables.add(GraphRow(report, Stat.STANDARD_DEVIATION)) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.WIN_RATIO), it.computedStat(Stat.MAXIMUM_NETRESULT))) rowRepresentables.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.volume)) - rowRepresentables.add(GraphRow(report, Stat.NET_RESULT)) + rowRepresentables.add(GraphRow(report, Stat.DURATION)) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.DURATION), it.computedStat(Stat.AVERAGE_DURATION))) rowRepresentables.add(StatDoubleRow(it.computedStat(Stat.DAYS_PLAYED), it.computedStat(Stat.MAXIMUM_DURATION))) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt index 62fabad5..9509cf81 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt @@ -17,7 +17,6 @@ import net.pokeranalytics.android.R import net.pokeranalytics.android.calculus.* import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment -import net.pokeranalytics.android.ui.graph.PALineDataSet import net.pokeranalytics.android.ui.graph.setStyle import net.pokeranalytics.android.ui.view.LegendView import kotlin.coroutines.CoroutineContext @@ -46,7 +45,7 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co private lateinit var parentActivity: PokerAnalyticsActivity private lateinit var selectedReport: Report private lateinit var legendView: LegendView - private lateinit var chartView: BarLineChartBase<*> + private var chartView: BarLineChartBase<*>? = null private var stat: Stat = Stat.NET_RESULT private var aggregationType: AggregationType = AggregationType.SESSION @@ -65,33 +64,20 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co loadGraph() } - // OnChartValueSelectedListener - override fun onNothingSelected() { - // nothing to do - } - - override fun onValueSelected(e: Entry?, h: Highlight?) { - e?.let { entry -> - val statEntry = when (entry.data) { - is ObjectIdentifier -> { - val identifier = entry.data as ObjectIdentifier - getRealm().where(identifier.clazz).equalTo("id", identifier.id).findAll().firstOrNull() - } - is StatEntry -> entry.data as StatEntry? - else -> null - } - statEntry?.let { + /** + * Set data + */ + fun setData(report: Report, stat: Stat, aggregationType: AggregationType) { - val formattedDate = it.entryTitle - val entryValue = it.formattedValue(this.stat, requireContext()) - val totalStatValue = this.stat.format(entry.y.toDouble(), currency = null) + this.selectedReport = report + this.aggregationType = aggregationType + this.stat = stat - this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue) - } + if (isAdded && !isDetached) { + loadGraph() } } - /** * Init UI */ @@ -103,14 +89,6 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co this.legendView = LegendView(requireContext()) this.legendContainer.addView(this.legendView) - this.chartView = when (stat.graphType) { - GraphType.LINE -> LineChart(context) - GraphType.BAR -> BarChart(context) - } - - val axisFormatting = aggregationType.axisFormatting - this.chartView.setStyle(false, axisFormatting, requireContext()) - this.chartContainer.addView(this.chartView) } /** @@ -118,38 +96,41 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co */ private fun loadGraph() { - val graphEntries = when (aggregationType) { - AggregationType.SESSION -> selectedReport.results.firstOrNull()?.defaultStatEntries(stat) + val ds = when (aggregationType) { + AggregationType.SESSION -> selectedReport.results.firstOrNull()?.defaultStatEntries(stat, requireContext()) AggregationType.DURATION -> { - selectedReport.results.firstOrNull()?.durationEntries(stat) + selectedReport.results.firstOrNull()?.durationEntries(stat, requireContext()) } AggregationType.MONTH, AggregationType.YEAR -> { when (this.stat) { Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> selectedReport.barEntries(this.stat) - else -> selectedReport.lineEntries(this.stat) + else -> selectedReport.lineEntries(this.stat, requireContext()) } } } - graphEntries?.let { entries -> + ds?.let { dataSet -> - this.legendView.prepareWithStat(this.stat, entries.size) + this.legendView.prepareWithStat(this.stat, dataSet.entryCount) - when (stat.graphType) { - GraphType.LINE -> { - val lineChart: LineChart = this.chartView as LineChart + // initialize chart + if (this.chartView == null) { + this.chartView = when (dataSet) { + is LineDataSet -> LineChart(context) + is BarDataSet -> BarChart(context) + else -> null + } + this.chartContainer.addView(this.chartView) + } - val dataSet = PALineDataSet(entries, this.stat.name, requireContext()) - val colors = arrayOf(R.color.green_light).toIntArray() - dataSet.setColors(colors, context) - dataSet.setDrawCircles(false) + when (dataSet) { + is LineDataSet -> { + val lineChart: LineChart = this.chartView as LineChart val lineData = LineData(listOf(dataSet)) - lineChart.data = lineData } - GraphType.BAR -> { + is BarDataSet -> { val barChart = this.chartView as BarChart - val dataSet = BarDataSet(entries as List, this.stat.name) val colors = arrayOf(R.color.green_light).toIntArray() dataSet.setColors(colors, context) val barData = BarData(listOf(dataSet)) @@ -158,26 +139,48 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co } val axisFormatting = aggregationType.axisFormatting - this.chartView.setStyle(false, axisFormatting, requireContext()) - this.chartView.setOnChartValueSelectedListener(this) - this.chartView.highlightValue((entries.size - 1).toFloat(), 0) + this.chartView?.let { + it.setStyle(false, axisFormatting, requireContext()) + it.setOnChartValueSelectedListener(this) + } + + this.selectValue(dataSet.getEntryForIndex(dataSet.entryCount - 1)) + } } - /** - * Set data - */ - fun setData(report: Report, stat: Stat, aggregationType: AggregationType) { + // OnChartValueSelectedListener + override fun onNothingSelected() { + // nothing to do + } - this.selectedReport = report - this.aggregationType = aggregationType - this.stat = stat + override fun onValueSelected(e: Entry?, h: Highlight?) { + e?.let { entry -> + this.selectValue(entry) + } + } - if (isAdded && !isDetached) { - loadGraph() + private fun selectValue(entry: Entry) { + + val statEntry = when (entry.data) { + is ObjectIdentifier -> { + val identifier = entry.data as ObjectIdentifier + getRealm().where(identifier.clazz).equalTo("id", identifier.id).findAll().firstOrNull() + } + is StatEntry -> entry.data as StatEntry? + else -> null + } + statEntry?.let { + + val formattedDate = it.entryTitle + val entryValue = it.formattedValue(this.stat, requireContext()) + val totalStatValue = this.stat.format(entry.y.toDouble(), currency = null) + + this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue) } + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/graph/ChartDataSet.kt b/app/src/main/java/net/pokeranalytics/android/ui/graph/ChartDataSet.kt index 71e7068a..2338ad35 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/graph/ChartDataSet.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/graph/ChartDataSet.kt @@ -10,10 +10,17 @@ class PALineDataSet(yVals: List, label: String, context: Context) : LineD init { this.highLightColor = context.getColor(R.color.chart_highlight_indicator) this.setDrawValues(false) + this.setDrawCircles(false) + + val colors = arrayOf(R.color.green_light).toIntArray() + this.setColors(colors, context) + } } + + //class PABarDataSet(yVals: List, label: String, context: Context) : BarDataSet(yVals, label) { // // init { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index fd5040cc..f832bf98 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -13,17 +13,18 @@ import androidx.core.widget.ContentLoadingProgressBar import androidx.recyclerview.widget.RecyclerView import com.github.mikephil.charting.charts.BarChart import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet import kotlinx.android.synthetic.main.row_history_session.view.* import kotlinx.android.synthetic.main.row_transaction.view.* import net.pokeranalytics.android.R -import net.pokeranalytics.android.calculus.GraphType import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.extensions.setTextFormat import net.pokeranalytics.android.ui.graph.AxisFormatting -import net.pokeranalytics.android.ui.graph.PALineDataSet import net.pokeranalytics.android.ui.graph.setStyle import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.GraphRow @@ -149,9 +150,7 @@ enum class RowViewType(private var layoutRes: Int) { val listener = View.OnClickListener { adapter.delegate?.onRowSelected(position, row) } - itemView.findViewById(R.id.container)?.let { - it.setOnClickListener(listener) - } + itemView.findViewById(R.id.container)?.setOnClickListener(listener) } } @@ -189,9 +188,7 @@ enum class RowViewType(private var layoutRes: Int) { } } - itemView.findViewById(R.id.container)?.let { - it.setOnClickListener(listener) - } + itemView.findViewById(R.id.container)?.setOnClickListener(listener) } // Switch @@ -318,31 +315,24 @@ enum class RowViewType(private var layoutRes: Int) { BindableHolder { override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { - //TODO: Implementation - if (row is GraphRow) { - row.report.results.firstOrNull()?.defaultStatEntries(row.stat)?.let { entries -> + row.report.results.firstOrNull()?.defaultStatEntries(row.stat, itemView.context)?.let { dataSet -> val context = itemView.context - val dataSet = PALineDataSet(entries, row.stat.name, context) - val colors = arrayOf(R.color.green_light).toIntArray() - dataSet.setColors(colors, context) - dataSet.setDrawCircles(false) - dataSet.setDrawValues(false) - val lineData = LineData(listOf(dataSet)) - - val chartView = when (row.stat.graphType) { - GraphType.LINE -> { + val chartView = when (dataSet) { + is LineDataSet -> { val lineChart = LineChart(context) - lineChart.data = lineData + lineChart.data = LineData(dataSet) lineChart } - GraphType.BAR -> { + is BarDataSet -> { val barChart = BarChart(context) + barChart.data = BarData(dataSet) barChart } + else -> null } itemView.findViewById(R.id.chartContainer)?.let { @@ -350,9 +340,12 @@ enum class RowViewType(private var layoutRes: Int) { it.addView(chartView) } - chartView.setStyle(true, AxisFormatting.DEFAULT, context) - chartView.setTouchEnabled(false) - chartView.highlightValue((entries.size - 1).toFloat(), 0) + chartView?.let { + chartView.setStyle(true, AxisFormatting.DEFAULT, context) + chartView.setTouchEnabled(false) + } + +// chartView.highlightValue((entries.size - 1).toFloat(), 0) } } diff --git a/app/src/main/java/net/pokeranalytics/android/util/FakeDataManager.kt b/app/src/main/java/net/pokeranalytics/android/util/FakeDataManager.kt index 2ccbadf6..e9a5603a 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/FakeDataManager.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/FakeDataManager.kt @@ -77,7 +77,7 @@ class FakeDataManager { session.result?.let { result -> val buyin = buyinList.random() result.buyin = buyinList.random() - result.netResult = resultsList.random() + buyin + result.cashout = resultsList.random() + buyin } }