diff --git a/app/src/androidTest/java/net/pokeranalytics/android/components/SessionInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/components/SessionInstrumentedUnitTest.kt new file mode 100644 index 00000000..fd6cf759 --- /dev/null +++ b/app/src/androidTest/java/net/pokeranalytics/android/components/SessionInstrumentedUnitTest.kt @@ -0,0 +1,44 @@ +package net.pokeranalytics.android.components + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.realm.RealmList +import net.pokeranalytics.android.model.realm.* +import org.junit.runner.RunWith +import java.util.* + +@RunWith(AndroidJUnit4::class) +open class SessionInstrumentedUnitTest : RealmInstrumentedUnitTest() { + + // convenience extension + fun Session.Companion.testInstance( + netResult: Double = 0.0, + isTournament: Boolean = false, + startDate: Date = Date(), + endDate: Int = 1, + bankroll: Bankroll? = null, + game: Game? = null, + location: Location? = null, + tournamentName: TournamentName? = null, + tournamentFeatures: RealmList = RealmList(), + numberOfTable: Int = 1, + limit: Int? = null, + tableSize: Int? = null + ): Session { + val session: Session = Session.newInstance(super.mockRealm, isTournament, bankroll) + session.game = game + session.location = location + session.tournamentFeatures = tournamentFeatures + session.tournamentName = tournamentName + session.limit = limit + session.numberOfTables = numberOfTable + session.tableSize = tableSize + session.startDate = startDate + session.result?.netResult = netResult + val cal = Calendar.getInstance() // creates calendar + cal.time = startDate // sets calendar time/date + cal.add(Calendar.HOUR_OF_DAY, endDate) // adds one hour + session.endDate = cal.time // returns new date object, one hour in the future + return session + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatPerformanceUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatPerformanceUnitTest.kt new file mode 100644 index 00000000..e7b9773b --- /dev/null +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatPerformanceUnitTest.kt @@ -0,0 +1,34 @@ +package net.pokeranalytics.android.unitTests + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import net.pokeranalytics.android.components.SessionInstrumentedUnitTest +import net.pokeranalytics.android.model.realm.Result +import net.pokeranalytics.android.model.realm.Session +import org.junit.Test +import org.junit.runner.RunWith +import java.util.* + +@RunWith(AndroidJUnit4::class) +class StatPerformanceUnitTest : SessionInstrumentedUnitTest() { + + @Test + fun testSessionNetResultOnLoad() { + val realm = mockRealm + realm.beginTransaction() + + for (index in 0..100) { + Session.testInstance((-2000..2000).random().toDouble()) + println("*** creating $index") + } + + realm.commitTransaction() + + val d1 = Date() + realm.where(Result::class.java).sum("netResult") + + val d2 = Date() + val duration = (d2.time - d1.time) + println("*** ended in $duration milliseconds") + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt index 374cadf6..5c27d92c 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt @@ -1,13 +1,13 @@ package net.pokeranalytics.android.unitTests import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.realm.RealmList import io.realm.RealmResults import net.pokeranalytics.android.calculus.Calculator import net.pokeranalytics.android.calculus.ComputableGroup import net.pokeranalytics.android.calculus.ComputedResults import net.pokeranalytics.android.calculus.Stat -import net.pokeranalytics.android.components.RealmInstrumentedUnitTest +import net.pokeranalytics.android.components.SessionInstrumentedUnitTest +import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.Currency import org.junit.Assert @@ -23,62 +23,10 @@ import java.util.* * See [testing documentation](http://d.android.com/tools/testing). */ @RunWith(AndroidJUnit4::class) -class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() { - - // convenience extension - private fun Session.Companion.testInstance( - netResult: Double = 0.0, - isTournament: Boolean = false, - startDate: Date = Date(), - endDate: Int = 1, - bankroll: Bankroll? = null, - game: Game? = null, - location: Location? = null, - tournamentName: TournamentName? = null, - tournamentFeatures: RealmList = RealmList(), - numberOfTable: Int = 1, - limit: Int? = null, - tableSize: Int? = null - ): Session { - val session: Session = Session.newInstance(super.mockRealm, isTournament, bankroll) - session.game = game - session.location = location - session.tournamentFeatures = tournamentFeatures - session.tournamentName = tournamentName - session.limit = limit - session.numberOfTables = numberOfTable - session.tableSize = tableSize - session.startDate = startDate - session.result?.netResult = netResult - val cal = Calendar.getInstance() // creates calendar - cal.time = startDate // sets calendar time/date - cal.add(Calendar.HOUR_OF_DAY, endDate) // adds one hour - session.endDate = cal.time // returns new date object, one hour in the future - return session - } - - @Test - fun testSessionNetResultOnLoad() { - val realm = mockRealm - realm.beginTransaction() - - for (index in 0..100) { - Session.testInstance((-2000..2000).random().toDouble()) - println("*** creating $index") - } - - realm.commitTransaction() - - val d1 = Date() - realm.where(Result::class.java).sum("netResult") - - val d2 = Date() - val duration = (d2.time - d1.time) - println("*** ended in $duration milliseconds") - } +class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() { @Test - fun testSessionStats() { + fun testAllSessionStats() { val realm = this.mockRealm @@ -124,8 +72,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() { println(">>>>>> rated net = ${it.ratedNet} ") } - val stats: List = listOf(Stat.NETRESULT, Stat.AVERAGE) - val group = ComputableGroup("test", listOf(), stats) + val group = ComputableGroup("test") val options = Calculator.Options() options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION, @@ -510,12 +457,12 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() { val sets = realm.where(SessionSet::class.java).findAll() - Assert.assertEquals(1, sets.size) + assertEquals(1, sets.size) val set = sets.first() if (set != null) { - Assert.assertEquals(sd1.time, set.startDate.time) - Assert.assertEquals(ed1.time, set.endDate.time) + assertEquals(sd1.time, set.startDate.time) + assertEquals(ed1.time, set.endDate.time) } else { Assert.fail("No set") } @@ -712,7 +659,6 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() { } - @Test fun testDaysPlayed() { @@ -748,4 +694,95 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() { } } + + @Test + fun testFilteredHourlyRate() { + + val realm = this.mockRealm + + realm.executeTransaction { + + val s1 = newSessionInstance(realm, true) + val s2 = newSessionInstance(realm, true) + val s3 = newSessionInstance(realm, false) + + val sdf = SimpleDateFormat("dd/M/yyyy hh:mm") + + val sd1 = sdf.parse("01/1/2019 09:00") + val ed1 = sdf.parse("01/1/2019 10:00") + val sd2 = sdf.parse("01/1/2019 04:00") + val ed2 = sdf.parse("01/1/2019 05:00") + val sd3 = sdf.parse("01/1/2019 03:00") + val ed3 = sdf.parse("01/1/2019 11:00") + + s1.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + s3.startDate = sd3 + s3.endDate = ed3 + + } + + val group = ComputableGroup("test", listOf(QueryCondition.CASH)) + + val options = Calculator.Options() + options.displayedStats = listOf(Stat.DURATION) + + val results: ComputedResults = Calculator.compute(realm, group, options) + val delta = 0.01 + + val duration = results.computedStat(Stat.DURATION) + if (duration != null) { + assertEquals(2.0, duration.value, delta) + } else { + Assert.fail("No Net result stat") + } + } + + @Test + fun testFilteredHourlyRate2() { + + val realm = this.mockRealm + + realm.executeTransaction { + + val s1 = newSessionInstance(realm, true) + val s2 = newSessionInstance(realm, true) + val s3 = newSessionInstance(realm, false) + + val sdf = SimpleDateFormat("dd/M/yyyy hh:mm") + + val sd1 = sdf.parse("01/1/2019 06:00") + val ed1 = sdf.parse("01/1/2019 09:00") + val sd2 = sdf.parse("01/1/2019 07:00") + val ed2 = sdf.parse("01/1/2019 10:00") + val sd3 = sdf.parse("01/1/2019 03:00") + val ed3 = sdf.parse("01/1/2019 11:00") + + s1.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + s3.startDate = sd3 + s3.endDate = ed3 + + } + + val group = ComputableGroup("test", listOf(QueryCondition.CASH)) + + val options = Calculator.Options() + options.displayedStats = listOf(Stat.DURATION) + + val results: ComputedResults = Calculator.compute(realm, group, options) + val delta = 0.01 + + val duration = results.computedStat(Stat.DURATION) + if (duration != null) { + assertEquals(4.0, duration.value, delta) + } else { + Assert.fail("No Net result stat") + } + } + } \ No newline at end of file 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 4960638f..bc784814 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt @@ -4,7 +4,9 @@ import io.realm.Realm import net.pokeranalytics.android.calculus.Stat.* import net.pokeranalytics.android.model.comparison.Comparator import net.pokeranalytics.android.model.comparison.combined +import net.pokeranalytics.android.model.extensions.hourlyDuration import net.pokeranalytics.android.model.filter.QueryCondition +import net.pokeranalytics.android.model.filter.filter import net.pokeranalytics.android.model.filter.name import net.pokeranalytics.android.model.realm.ComputableResult import net.pokeranalytics.android.model.realm.SessionSet @@ -65,9 +67,18 @@ class Calculator { return false } - val computeLongestStreak = this.displayedStats.contains(LONGEST_STREAKS) - val computeLocationsPlayed = this.displayedStats.contains(LOCATIONS_PLAYED) - val computeDaysPlayed = this.displayedStats.contains(DAYS_PLAYED) + val computeLongestStreak: Boolean + get() { + return this.displayedStats.contains(LONGEST_STREAKS) + } + val computeLocationsPlayed: Boolean + get() { + return this.displayedStats.contains(LOCATIONS_PLAYED) + } + val computeDaysPlayed: Boolean + get() { + return this.displayedStats.contains(DAYS_PLAYED) + } } @@ -137,12 +148,12 @@ class Calculator { group.cleanup() // Computes actual sessionGroup stats - val results: ComputedResults = Calculator.compute(realm, group, options = options) + val results: ComputedResults = this.compute(realm, group, options = options) // Computes the compared sessionGroup if existing val comparedGroup = group.comparedComputables if (comparedGroup != null) { - val comparedResults = Calculator.compute(realm, comparedGroup, options = options) + val comparedResults = this.compute(realm, comparedGroup, options = options) group.comparedComputedResults = comparedResults results.computeStatVariations(comparedResults) } @@ -200,7 +211,9 @@ class Calculator { var tWinningSessionCount = 0 var tBuyinSum = 0.0 var tHands = 0.0 - var longestWinStreak = 0; var longestLoseStreak = 0; var currentStreak = 0 + var longestWinStreak = 0; + var longestLoseStreak = 0; + var currentStreak = 0 computables.forEach { computable -> index++ @@ -263,39 +276,49 @@ class Calculator { val sessionSets = computableGroup.sessionSets(realm) - // Compute for each serie - val gHourlyDuration = - sessionSets.sum(SessionSet.Field.NET_DURATION.identifier).toDouble() / 3600000 // (milliseconds to hours) - val gSum = sessionSets.sum(SessionSet.Field.RATED_NET.identifier).toDouble() - val gTotalHands = sessionSets.sum(SessionSet.Field.ESTIMATED_HANDS.identifier).toDouble() - val gBBSum = sessionSets.sum(SessionSet.Field.BB_NET.identifier).toDouble() - val maxDuration = sessionSets.max(SessionSet.Field.NET_DURATION.identifier)?.toDouble() + var gHourlyDuration: Double? = null + var gBBSum: Double? = null + var maxDuration: Double? = null - val hourlyRate = gSum / gHourlyDuration -// var bbHourlyRate = gBBSum / gDuration + if (computableGroup.conditions.size == 0) { // SessionSets are fine + gHourlyDuration = + sessionSets.sum(SessionSet.Field.NET_DURATION.identifier).toDouble() / 3600000 // (milliseconds to hours) + gBBSum = sessionSets.sum(SessionSet.Field.BB_NET.identifier).toDouble() - val shouldIterateOverSets = (options.evolutionValues != Options.EvolutionValues.NONE || options.computeDaysPlayed) + sessionSets.max(SessionSet.Field.NET_DURATION.identifier)?.let { + maxDuration = it.toDouble() / 3600000 + } + } + + val shouldIterateOverSets = computableGroup.conditions.size > 0 || + options.evolutionValues != Options.EvolutionValues.NONE || + options.computeDaysPlayed if (shouldIterateOverSets) { var tHourlyDuration = 0.0 var tIndex = 0 - var tSum = 0.0 - var tTotalHands = 0.0 + var tRatedNetSum = 0.0 var tBBSum = 0.0 + var tTotalHands = 0.0 var tHourlyRate = 0.0 var tHourlyRateBB = 0.0 val daysSet = mutableSetOf() + var tMaxDuration = 0.0 sessionSets.forEach { sessionSet -> tIndex++ - tHourlyDuration += sessionSet.hourlyDuration - tSum += sessionSet.ratedNet - tTotalHands += sessionSet.estimatedHands - tBBSum += sessionSet.bbNet - tHourlyRate = gSum / tHourlyDuration - tHourlyRateBB = gBBSum / tHourlyDuration + val setStats = SSStats(sessionSet, computableGroup.conditions) + + tRatedNetSum += setStats.ratedNet + tBBSum += setStats.bbSum + tHourlyDuration += setStats.hourlyDuration + tTotalHands += setStats.estimatedHands + tMaxDuration = max(tMaxDuration, setStats.hourlyDuration) + + tHourlyRate = tRatedNetSum / tHourlyDuration + tHourlyRateBB = tBBSum / tHourlyDuration daysSet.add(sessionSet.startDate.startOfDay()) when (options.evolutionValues) { @@ -314,13 +337,13 @@ class Calculator { ) results.addEvolutionValue(tHourlyRateBB, stat = HOURLY_RATE_BB, data = sessionSet) - Stat.netBBPer100Hands(gBBSum, gTotalHands)?.let { netBB100 -> + Stat.netBBPer100Hands(tBBSum, tTotalHands)?.let { netBB100 -> results.addEvolutionValue(netBB100, stat = NET_BB_PER_100_HANDS, data = sessionSet) } } Options.EvolutionValues.TIMED -> { - results.addEvolutionValue(tSum, tHourlyDuration, NETRESULT, sessionSet) + results.addEvolutionValue(tRatedNetSum, tHourlyDuration, NETRESULT, sessionSet) results.addEvolutionValue(tHourlyRate, tHourlyDuration, HOURLY_RATE, sessionSet) results.addEvolutionValue( tIndex.toDouble(), @@ -342,7 +365,7 @@ class Calculator { ) results.addEvolutionValue(tHourlyRateBB, tHourlyDuration, HOURLY_RATE_BB, sessionSet) - Stat.netBBPer100Hands(gBBSum, gTotalHands)?.let { netBB100 -> + Stat.netBBPer100Hands(tBBSum, tTotalHands)?.let { netBB100 -> results.addEvolutionValue( netBB100, tHourlyDuration, @@ -351,14 +374,24 @@ class Calculator { ) } } + else -> { + // nothing + } } results.addStat(DAYS_PLAYED, daysSet.size.toDouble()) } + + gHourlyDuration = tHourlyDuration + gBBSum = tBBSum + maxDuration = tMaxDuration + } var average = 0.0 + var hourlyRate = 0.0 + if (computables.size > 0) { average = sum / computables.size.toDouble() val winRatio = winningSessionCount.toDouble() / computables.size.toDouble() @@ -373,25 +406,30 @@ class Calculator { ) } - if (sessionSets.size > 0) { - val avgDuration = gHourlyDuration / sessionSets.size - results.addStats( - setOf( - ComputedStat(HOURLY_RATE, hourlyRate), - ComputedStat(AVERAGE_DURATION, avgDuration) - ) - ) + if (gHourlyDuration != null) { + + hourlyRate = sum / gHourlyDuration + if (sessionSets.size > 0) { + val avgDuration = gHourlyDuration / sessionSets.size + results.addStat(HOURLY_RATE, hourlyRate) + results.addStat(AVERAGE_DURATION, avgDuration) + } + results.addStat(DURATION, gHourlyDuration) + } + + if (gBBSum != null) { + if (gHourlyDuration != null) { + results.addStat(HOURLY_RATE_BB, gBBSum / gHourlyDuration) + } + results.addStat(AVERAGE_NET_BB, gBBSum / bbSessionCount) } // Create stats results.addStats( setOf( ComputedStat(NETRESULT, sum), - ComputedStat(DURATION, gHourlyDuration), ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()), ComputedStat(NUMBER_OF_GAMES, computables.size.toDouble()), - ComputedStat(HOURLY_RATE_BB, gBBSum / gHourlyDuration), - ComputedStat(AVERAGE_NET_BB, gBBSum / bbSessionCount), ComputedStat(HANDS_PLAYED, totalHands) ) ) @@ -410,7 +448,7 @@ class Calculator { results.addStat(MINIMUM_NETRESULT, min) } maxDuration?.let { maxd -> - results.addStat(MAXIMUM_DURATION, maxd / 3600000) // (milliseconds to hours) + results.addStat(MAXIMUM_DURATION, maxd) // (milliseconds to hours) } val bbPer100Hands = bbSum / totalHands * 100 @@ -428,20 +466,22 @@ class Calculator { val standardDeviation = Math.sqrt(stdSum / computables.size) val standardDeviationBBper100Hands = Math.sqrt(stdBBper100HandsSum / computables.size) + results.addStat(STANDARD_DEVIATION, standardDeviation) + results.addStat(STANDARD_DEVIATION_BB_PER_100_HANDS, standardDeviationBBper100Hands) + // Session Set - var hourlyStdSum = 0.0 - sessionSets.forEach { set -> - hourlyStdSum += Math.pow(set.hourlyRate - hourlyRate, 2.0) + if (gHourlyDuration != null) { + var hourlyStdSum = 0.0 + sessionSets.forEach { set -> + val ssStats = SSStats(set, computableGroup.conditions) + val sHourlyRate = ssStats.hourlyRate + hourlyStdSum += Math.pow(sHourlyRate - hourlyRate, 2.0) + } + val hourlyStandardDeviation = Math.sqrt(hourlyStdSum / sessionSets.size) + + results.addStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation) } - val hourlyStandardDeviation = Math.sqrt(hourlyStdSum / sessionSets.size) - results.addStats( - setOf( - ComputedStat(STANDARD_DEVIATION, standardDeviation), - ComputedStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation), - ComputedStat(STANDARD_DEVIATION_BB_PER_100_HANDS, standardDeviationBBper100Hands) - ) - ) } return results @@ -450,4 +490,43 @@ class Calculator { } +} + +class SSStats(sessionSet: SessionSet, conditions: List) { // Session Set Stats + + var hourlyDuration: Double = 0.0 + var estimatedHands: Double = 0.0 + var bbSum: Double = 0.0 + var ratedNet: Double = 0.0 + + val hourlyRate: Double + get() { + return this.ratedNet / this.hourlyDuration + } + + init { + + if (sessionSet.sessions?.size == 1) { // use precomputed values + this.initStatsWithSet(sessionSet) + } else { // dynamically filter and compute subset + val setSessions = sessionSet.sessions!! + val filteredSessions = setSessions.filter(conditions) + if (setSessions.size == filteredSessions.size) { + this.initStatsWithSet(sessionSet) + } else { + ratedNet = filteredSessions.sumByDouble { it.computableResult?.ratedNet ?: 0.0 } + bbSum = filteredSessions.sumByDouble { it.bbNet } + hourlyDuration = filteredSessions.hourlyDuration + estimatedHands = filteredSessions.sumByDouble { it.estimatedHands } + } + } + } + + private fun initStatsWithSet(sessionSet: SessionSet) { + ratedNet = sessionSet.ratedNet + bbSum = sessionSet.bbNet + hourlyDuration = sessionSet.hourlyDuration + estimatedHands = sessionSet.estimatedHands + } + } \ No newline at end of file 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 effdd611..b0e813a6 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt @@ -1,5 +1,6 @@ package net.pokeranalytics.android.calculus +import android.content.Context import com.github.mikephil.charting.data.BarEntry import com.github.mikephil.charting.data.Entry import io.realm.Realm @@ -35,13 +36,26 @@ class Report() { this._results.add(result) } + fun lineEntries(stat: Stat): List { + val entries = mutableListOf() + + this._results.forEachIndexed { index, results -> + val cs = results.computedStat(stat) + cs?.let { computedStat -> + entries.add(Entry(index.toFloat(), computedStat.value.toFloat(), results)) + } + } + return entries + } + fun barEntries(stat: Stat): List { val entries = mutableListOf() this._results.forEachIndexed { index, results -> val cs = results.computedStat(stat) cs?.let { computedStat -> - entries.add(Entry(index.toFloat(), computedStat.value.toFloat(), results.group)) + val barEntry = BarEntry(index.toFloat(), computedStat.value.toFloat(), results) + entries.add(barEntry) } } return entries @@ -61,7 +75,7 @@ class Report() { /** * A sessionGroup of computable items identified by a name */ -class ComputableGroup(name: String, conditions: List, stats: List? = null) { +class ComputableGroup(name: String, conditions: List = listOf(), stats: List? = null) { /** * The display name of the group @@ -140,9 +154,10 @@ class ComputableGroup(name: String, conditions: List, stats: Lis get() { return this._computables?.isEmpty() ?: true } + } -class ComputedResults(group: ComputableGroup) { +class ComputedResults(group: ComputableGroup) : StatEntry { /** * The session group used to computed the stats @@ -268,6 +283,19 @@ class ComputedResults(group: ComputableGroup) { val isEmpty: Boolean = this.group.isEmpty + + // Stat Entry + + override val entryTitle: String = this.group.name + + override fun formattedValue(stat: Stat, context: Context): TextFormat { + this.computedStat(stat)?.let { + return it.format(context) + } ?: run { + throw IllegalStateException("Missing stat in results") + } + } + } class Point(val x: Double, val y: Double, val data: Any) { 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 4f1782da..51ddc180 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -3,7 +3,6 @@ package net.pokeranalytics.android.calculus import android.content.Context import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.FormattingException -import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Timed import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType @@ -21,8 +20,10 @@ class ObjectIdentifier(var id: String, var clazz: Class) { } -interface StatBase : Identifiable { +interface StatEntry { + + val entryTitle: String fun formattedValue(stat: Stat, context: Context): TextFormat } diff --git a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt index f1ff2e51..2314b31b 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -11,13 +11,13 @@ class ConfigurationException(message: String) : Exception(message) sealed class PokerAnalyticsException(message: String) : Exception(message) { object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryCondition") object FilterElementUnknownSectionName: PokerAnalyticsException(message = "No filterElement section name was found to identify the queryCondition") - object FilterMissingEntity: PokerAnalyticsException(message = "This filter has no entity initialized") + object FilterMissingEntity: PokerAnalyticsException(message = "This queryWith has no entity initialized") object FilterUnhandledEntity : PokerAnalyticsException(message = "This entity is not filterable") object QueryValueMapUnknown: PokerAnalyticsException(message = "fieldName is missing") - object QueryTypeUnhandled: PokerAnalyticsException(message = "filter type not handled") + object QueryTypeUnhandled: PokerAnalyticsException(message = "queryWith type not handled") object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected") - object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "filter is empty or null") - data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "filter element '$filterElementRow' type is missing") + object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "queryWith is empty or null") + data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "queryWith element '$filterElementRow' type is missing") data class QueryValueMapMissingKeys(val missingKeys: List) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys") - data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no filter type for $filterElementRow") + data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no queryWith type for $filterElementRow") } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/comparison/Comparator.kt b/app/src/main/java/net/pokeranalytics/android/model/comparison/Comparator.kt index fbb51d8d..7273fddc 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/comparison/Comparator.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/comparison/Comparator.kt @@ -1,11 +1,9 @@ package net.pokeranalytics.android.model.comparison import io.realm.Realm -import io.realm.RealmQuery import io.realm.Sort import io.realm.kotlin.where import net.pokeranalytics.android.exceptions.PokerAnalyticsException -import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.realm.* import java.util.* @@ -177,14 +175,6 @@ fun List.combined(): List> { return getCombinations(comparatorList) } -inline fun List.query(realm: Realm): RealmQuery { - var realmQuery = realm.where() - this.forEach { - realmQuery = it.filter(realmQuery) - } - return realmQuery -} - fun getCombinations(lists: List>): List> { var combinations: MutableSet> = LinkedHashSet() var newCombinations: MutableSet> diff --git a/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt b/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt index 02d9495b..a6caa598 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt @@ -25,9 +25,6 @@ enum class SessionState { */ fun Session.getState(): SessionState { -// if (timeFrame == null) { -// return SessionState.PENDING -// } val start = this.startDate if (start == null) { return SessionState.PENDING @@ -43,4 +40,59 @@ fun Session.getState(): SessionState { } } +} + +val AbstractList.hourlyDuration: Double + get() { + val intervals = mutableListOf() + this.forEach { + val interval = TimeInterval(it.startDate!!, it.endDate!!, it.breakDuration) + intervals.update(interval) + } + return intervals.sumByDouble { it.hourlyDuration } + } + +class TimeInterval(var start: Date, var end: Date, var breakDuration: Long) { + + val hourlyDuration: Double + get() { + val netDuration = end.time - start.time - breakDuration + return (netDuration / 3600000).toDouble() + } +} + +fun MutableList.update(timeInterval: TimeInterval): MutableList { + + val overlapped = this.filter { + + (it.start.before(timeInterval.start) && it.end.after(timeInterval.start)) || + (it.start.before(timeInterval.end) && it.end.after(timeInterval.end)) || + (it.start.after(timeInterval.start) && it.end.before(timeInterval.end)) + + } + + if (overlapped.size == 0) { // add + this.add(timeInterval) + } else { // update + + var start = timeInterval.start + var end = timeInterval.end + var breakDuration = timeInterval.breakDuration + + overlapped.forEach { + if (it.start.before(start)) { + start = it.start + } + if (it.end.after(end)) { + end = it.end + } + breakDuration = kotlin.math.max(it.breakDuration, breakDuration) + } + + this.removeAll(overlapped) + this.add(TimeInterval(start, end, breakDuration)) + + } + + return this } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt index a7a7f467..dd202e69 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt @@ -1,6 +1,7 @@ package net.pokeranalytics.android.model.filter import io.realm.RealmModel +import io.realm.RealmResults import net.pokeranalytics.android.model.realm.ComputableResult import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.SessionSet @@ -11,7 +12,7 @@ import net.pokeranalytics.android.model.realm.SessionSet * - filters can be applied to different type of objects: Sessions, Hands, Transactions... * - filters can be applied to a list of different type of objects (feed) * - * A filter is described by the following: + * A queryWith is described by the following: * - a data type: Session, Hands... * - a field: table size of a Session * - an operator: equal, >=, <... @@ -27,7 +28,7 @@ import net.pokeranalytics.android.model.realm.SessionSet * - multiple numericValues as 'OR' * * Also: - * A filter should be able to be converted into a Realm query + * A queryWith should be able to be converted into a Realm query * */ @@ -47,6 +48,10 @@ interface Filterable : RealmModel { } +inline fun RealmResults.filter(conditions: List) : RealmResults { + return conditions.queryWith(this.where()).findAll() +} + class FilterHelper { companion object { @@ -68,11 +73,13 @@ class FilterHelper { } + + // -//fun MutableList.filter(filter: FilterCondition) : List { +//fun MutableList.queryWith(queryWith: FilterCondition) : List { // -// return this.filter { f -> -// return@filter true +// return this.queryWith { f -> +// return@queryWith true // } //} diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt index cf5dac46..9d39f9ed 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt @@ -1,10 +1,7 @@ package net.pokeranalytics.android.model.filter import io.realm.RealmQuery -import io.realm.internal.Table import net.pokeranalytics.android.exceptions.PokerAnalyticsException -import net.pokeranalytics.android.model.Limit -import net.pokeranalytics.android.model.TableSize import net.pokeranalytics.android.model.realm.FilterCondition import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.util.extensions.endOfDay @@ -15,6 +12,18 @@ fun List.name() : String { return this.map { it.name }.joinToString(" / ") } +//inline fun List.query(realm: Realm): RealmQuery { +// return this.queryWith(realm.where()) +//} + +inline fun List.queryWith(query: RealmQuery): RealmQuery { + var realmQuery = query + this.forEach { + realmQuery = it.queryWith(realmQuery) + } + return realmQuery +} + /** * Enum describing the way a query should be handled * Some queries requires a value to be checked upon through equals, in, more, less, between @@ -194,7 +203,7 @@ sealed class QueryCondition(var operator: Operator? = null) { * main method of the enum * providing a base RealmQuery [realmQuery], the method is able to attached the corresponding query and returns the newly formed [RealmQuery] */ - inline fun filter(realmQuery: RealmQuery): RealmQuery { + inline fun queryWith(realmQuery: RealmQuery): RealmQuery { val fieldName = FilterHelper.fieldNameForQueryType(this) fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown when (operator) { diff --git a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt index 49ba2542..82d0552f 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt @@ -1,10 +1,10 @@ package net.pokeranalytics.android.model.interfaces import net.pokeranalytics.android.calculus.ObjectIdentifier -import net.pokeranalytics.android.calculus.StatBase +import net.pokeranalytics.android.calculus.StatEntry import java.util.* -interface Timed : StatBase { +interface Timed : StatEntry, Identifiable { fun startDate() : Date? diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt index 9009b80b..3ac7f81b 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt @@ -35,7 +35,7 @@ open class Filter : RealmObject() { return realm.copyToRealm(filter) } - // Get a filter by its id + // Get a queryWith by its id fun getFilterBydId(realm: Realm, filterId: String): Filter? { return realm.where().equalTo("id", filterId).findFirst() } @@ -44,7 +44,7 @@ open class Filter : RealmObject() { inline fun queryOn(realm: Realm, queries: List): RealmResults { var realmQuery = realm.where() queries.forEach { - realmQuery = it.filter(realmQuery) + realmQuery = it.queryWith(realmQuery) } Timber.d(">>> Filter query: ${realmQuery.description}") return realmQuery.findAll() @@ -54,10 +54,10 @@ open class Filter : RealmObject() { @PrimaryKey var id = UUID.randomUUID().toString() - // the filter name + // the queryWith name var name: String = "" - // the number of use of the filter, + // the number of use of the queryWith, // for MutableRealmInteger, see https://realm.io/docs/java/latest/#counters val usageCount: MutableRealmInteger = MutableRealmInteger.valueOf(0) @@ -106,7 +106,7 @@ open class Filter : RealmObject() { } /** - * Set the saved value in the filter for the given [filterElementRow] + * Set the saved value in the queryWith for the given [filterElementRow] */ fun setSavedValueForElement(filterElementRow: FilterElementRow) { when (filterElementRow) { @@ -140,7 +140,7 @@ open class Filter : RealmObject() { this.filterConditions.map { it.queryCondition }.forEach { - realmQuery = it.filter(realmQuery) + realmQuery = it.queryWith(realmQuery) } return realmQuery.findAll() diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index 81a4d522..934528ee 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -35,6 +35,7 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.UserDefaults import net.pokeranalytics.android.util.extensions.* +import java.text.DateFormat import java.util.* import java.util.Currency import kotlin.collections.ArrayList @@ -42,7 +43,7 @@ import kotlin.collections.ArrayList typealias BB = Double open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDataSource, RowRepresentable, Timed, - TimeFilterable, Filterable { + TimeFilterable, Filterable { enum class Type { CASH_GAME, @@ -67,28 +68,28 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat LIVE, ONLINE -> "bankroll.live" CASH, TOURNAMENT -> "type" is BANKROLL -> "bankroll.id" - is GAME -> "game.id" - is TOURNAMENT_NAME -> "tournamentName.id" - is ANY_TOURNAMENT_FEATURES, is ALL_TOURNAMENT_FEATURES -> "tournamentFeatures.id" - is LOCATION -> "location.id" - is LIMIT -> "limit" - is TABLE_SIZE -> "tableSize" - is TOURNAMENT_TYPE -> "tournamentType" - is BLIND -> "blinds" - is COMMENT -> "comment" - is BETWEEN_NUMBER_OF_TABLE, is MORE_NUMBER_OF_TABLE, is LESS_NUMBER_OF_TABLE -> "numberOfTable" - is MORE_THAN_NET_RESULT, is LESS_THAN_NET_RESULT -> "computableResults.ratedNet" - is MORE_THAN_BUY_IN, is LESS_THAN_BUY_IN -> "result.buyin" - is MORE_THAN_CASH_OUT, is LESS_THAN_CASH_OUT -> "result.cashout" - is MORE_THAN_TIPS, is LESS_THAN_TIPS -> "result.tips" - is MORE_THAN_NUMBER_OF_PLAYER, is LESS_THAN_NUMBER_OF_PLAYER, is BETWEEN_NUMBER_OF_PLAYER -> "tournamentNumberOfPlayers" - is MORE_THAN_TOURNAMENT_FEE, is LESS_THAN_TOURNAMENT_FEE, is BETWEEN_TOURNAMENT_FEE -> "tournamentEntryFee" - is STARTED_FROM_DATE, is STARTED_TO_DATE -> "startDate" - is ENDED_FROM_DATE, is ENDED_TO_DATE -> "endDate" - is DAY_OF_WEEK, is WEEK_END, is WEEK_DAY -> "dayOfWeek" - is MONTH -> "month" - is YEAR -> "year" - TODAY, YESTERDAY, TODAY_AND_YESTERDAY, THIS_YEAR, THIS_MONTH, THIS_WEEK -> "startDate" + is GAME -> "game.id" + is TOURNAMENT_NAME -> "tournamentName.id" + is ANY_TOURNAMENT_FEATURES, is ALL_TOURNAMENT_FEATURES -> "tournamentFeatures.id" + is LOCATION -> "location.id" + is LIMIT -> "limit" + is TABLE_SIZE -> "tableSize" + is TOURNAMENT_TYPE -> "tournamentType" + is BLIND -> "blinds" + is COMMENT -> "comment" + is BETWEEN_NUMBER_OF_TABLE, is MORE_NUMBER_OF_TABLE, is LESS_NUMBER_OF_TABLE -> "numberOfTable" + is MORE_THAN_NET_RESULT, is LESS_THAN_NET_RESULT -> "computableResults.ratedNet" + is MORE_THAN_BUY_IN, is LESS_THAN_BUY_IN -> "result.buyin" + is MORE_THAN_CASH_OUT, is LESS_THAN_CASH_OUT -> "result.cashout" + is MORE_THAN_TIPS, is LESS_THAN_TIPS -> "result.tips" + is MORE_THAN_NUMBER_OF_PLAYER, is LESS_THAN_NUMBER_OF_PLAYER, is BETWEEN_NUMBER_OF_PLAYER -> "tournamentNumberOfPlayers" + is MORE_THAN_TOURNAMENT_FEE, is LESS_THAN_TOURNAMENT_FEE, is BETWEEN_TOURNAMENT_FEE -> "tournamentEntryFee" + is STARTED_FROM_DATE, is STARTED_TO_DATE -> "startDate" + is ENDED_FROM_DATE, is ENDED_TO_DATE -> "endDate" + is DAY_OF_WEEK, is WEEK_END, is WEEK_DAY -> "dayOfWeek" + is MONTH -> "month" + is YEAR -> "year" + TODAY, YESTERDAY, TODAY_AND_YESTERDAY, THIS_YEAR, THIS_MONTH, THIS_WEEK -> "startDate" else -> null } } @@ -114,7 +115,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat // Timed interface - override var dayOfWeek : Int? = null + override var dayOfWeek: Int? = null override var month: Int? = null override var year: Int? = null override var dayOfMonth: Int? = null @@ -213,21 +214,21 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat // The small blind value var cgSmallBlind: Double? = null - set(value) { - field = value - formatBlinds() - } + set(value) { + field = value + formatBlinds() + } // The big blind value var cgBigBlind: Double? = null set(value) { field = value this.computeStats() - formatBlinds() + formatBlinds() } - var blinds: String? = null - private set + var blinds: String? = null + private set // Tournament @@ -246,9 +247,9 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat // The features of the tournament, like Knockout, Shootout, Turbo... var tournamentFeatures: RealmList = RealmList() - fun bankrollHasBeenUpdated() { - formatBlinds() - } + fun bankrollHasBeenUpdated() { + formatBlinds() + } /** * Manages impacts on SessionSets @@ -303,7 +304,8 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat */ val bbNet: BB get() { - val bb = this.cgBigBlind; val result = this.result + val bb = this.cgBigBlind; + val result = this.result if (bb != null && result != null) { return result.net / bb } else { @@ -487,19 +489,19 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat return NULL_TEXT } - val hasDefaultCurrency: Boolean - get() { - return bankroll?.currency?.code == null - } + val hasDefaultCurrency: Boolean + get() { + return bankroll?.currency?.code == null + } - val currency : Currency - get() { - return bankroll?.currency?.code?.let { - Currency.getInstance(it) - } ?: run { - UserDefaults.currency - } - } + val currency: Currency + get() { + return bankroll?.currency?.code?.let { + Currency.getInstance(it) + } ?: run { + UserDefaults.currency + } + } /** * Return the game title @@ -518,18 +520,18 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat return if (gameTitle.isNotBlank()) gameTitle else NULL_TEXT } - fun getFormattedBlinds(): String { - return blinds ?: NULL_TEXT - } + fun getFormattedBlinds(): String { + return blinds ?: NULL_TEXT + } - private fun formatBlinds() { - blinds = null - if (cgBigBlind == null) return - cgBigBlind?.let { bb -> - val sb = cgSmallBlind ?: bb / 2.0 - blinds = "${currency.symbol} ${sb.formatted()}/${bb.round()}" - } - } + private fun formatBlinds() { + blinds = null + if (cgBigBlind == null) return + cgBigBlind?.let { bb -> + val sb = cgSmallBlind ?: bb / 2.0 + blinds = "${currency.symbol} ${sb.formatted()}/${bb.round()}" + } + } // LifeCycle @@ -569,7 +571,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } @Ignore - private var rowRepresentationForCurrentState : List = mutableListOf() + private var rowRepresentationForCurrentState: List = mutableListOf() private fun updatedRowRepresentationForCurrentState(): List { val rows = ArrayList() @@ -669,7 +671,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } SessionRow.TOURNAMENT_FEATURE -> { if (tournamentFeatures.size > 2) { - "${tournamentFeatures.subList(0,2).joinToString { + "${tournamentFeatures.subList(0, 2).joinToString { it.name }}, ..." } else if (tournamentFeatures.size > 0) { @@ -696,56 +698,98 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat override fun editDescriptors(row: RowRepresentable): ArrayList? { return when (row) { - SessionRow.BANKROLL -> row.editingDescriptors(mapOf( - "defaultValue" to this.bankroll, - "data" to LiveData.BANKROLL.items(realm))) - SessionRow.GAME -> row.editingDescriptors(mapOf( - "limit" to this.limit, - "defaultValue" to this.game, - "data" to LiveData.GAME.items(realm))) - SessionRow.LOCATION -> row.editingDescriptors(mapOf( - "defaultValue" to this.location, - "data" to LiveData.LOCATION.items(realm))) - SessionRow.TOURNAMENT_FEATURE -> row.editingDescriptors(mapOf( - "defaultValue" to this.tournamentFeatures, - "data" to LiveData.TOURNAMENT_FEATURE.items(realm))) - SessionRow.TOURNAMENT_NAME -> row.editingDescriptors(mapOf( - "defaultValue" to this.tournamentName, - "data" to LiveData.TOURNAMENT_NAME.items(realm))) - SessionRow.TOURNAMENT_TYPE -> row.editingDescriptors(mapOf( - "defaultValue" to this.tournamentType)) - SessionRow.TABLE_SIZE -> row.editingDescriptors(mapOf( - "defaultValue" to this.tableSize)) - SessionRow.BLINDS -> row.editingDescriptors(mapOf( - "sb" to cgSmallBlind?.round(), - "bb" to cgBigBlind?.round() - )) - SessionRow.BUY_IN -> row.editingDescriptors(mapOf( - "bb" to cgBigBlind, - "fee" to this.tournamentEntryFee, - "ratedBuyin" to result?.buyin - )) + SessionRow.BANKROLL -> row.editingDescriptors( + mapOf( + "defaultValue" to this.bankroll, + "data" to LiveData.BANKROLL.items(realm) + ) + ) + SessionRow.GAME -> row.editingDescriptors( + mapOf( + "limit" to this.limit, + "defaultValue" to this.game, + "data" to LiveData.GAME.items(realm) + ) + ) + SessionRow.LOCATION -> row.editingDescriptors( + mapOf( + "defaultValue" to this.location, + "data" to LiveData.LOCATION.items(realm) + ) + ) + SessionRow.TOURNAMENT_FEATURE -> row.editingDescriptors( + mapOf( + "defaultValue" to this.tournamentFeatures, + "data" to LiveData.TOURNAMENT_FEATURE.items(realm) + ) + ) + SessionRow.TOURNAMENT_NAME -> row.editingDescriptors( + mapOf( + "defaultValue" to this.tournamentName, + "data" to LiveData.TOURNAMENT_NAME.items(realm) + ) + ) + SessionRow.TOURNAMENT_TYPE -> row.editingDescriptors( + mapOf( + "defaultValue" to this.tournamentType + ) + ) + SessionRow.TABLE_SIZE -> row.editingDescriptors( + mapOf( + "defaultValue" to this.tableSize + ) + ) + SessionRow.BLINDS -> row.editingDescriptors( + mapOf( + "sb" to cgSmallBlind?.round(), + "bb" to cgBigBlind?.round() + ) + ) + SessionRow.BUY_IN -> row.editingDescriptors( + mapOf( + "bb" to cgBigBlind, + "fee" to this.tournamentEntryFee, + "ratedBuyin" to result?.buyin + ) + ) SessionRow.BREAK_TIME -> row.editingDescriptors(mapOf()) - SessionRow.CASHED_OUT, SessionRow.PRIZE -> row.editingDescriptors(mapOf( - "defaultValue" to result?.cashout - )) - SessionRow.NET_RESULT -> row.editingDescriptors(mapOf( - "defaultValue" to result?.netResult - )) - SessionRow.COMMENT -> row.editingDescriptors(mapOf( - "defaultValue" to this.comment)) - SessionRow.INITIAL_BUY_IN -> row.editingDescriptors(mapOf( - "defaultValue" to this.tournamentEntryFee - )) - SessionRow.PLAYERS -> row.editingDescriptors(mapOf( - "defaultValue" to this.tournamentNumberOfPlayers)) - SessionRow.POSITION -> row.editingDescriptors(mapOf( - "defaultValue" to this.result?.tournamentFinalPosition)) - SessionRow.TIPS -> row.editingDescriptors(mapOf( - "sb" to cgSmallBlind?.round(), - "bb" to cgBigBlind?.round(), - "tips" to result?.tips - )) + SessionRow.CASHED_OUT, SessionRow.PRIZE -> row.editingDescriptors( + mapOf( + "defaultValue" to result?.cashout + ) + ) + SessionRow.NET_RESULT -> row.editingDescriptors( + mapOf( + "defaultValue" to result?.netResult + ) + ) + SessionRow.COMMENT -> row.editingDescriptors( + mapOf( + "defaultValue" to this.comment + ) + ) + SessionRow.INITIAL_BUY_IN -> row.editingDescriptors( + mapOf( + "defaultValue" to this.tournamentEntryFee + ) + ) + SessionRow.PLAYERS -> row.editingDescriptors( + mapOf( + "defaultValue" to this.tournamentNumberOfPlayers + ) + ) + SessionRow.POSITION -> row.editingDescriptors( + mapOf( + "defaultValue" to this.result?.tournamentFinalPosition + ) + ) + SessionRow.TIPS -> row.editingDescriptors( + mapOf( + "sb" to cgSmallBlind?.round(), + "bb" to cgBigBlind?.round(), + "tips" to result?.tips + ) + ) else -> null } } @@ -782,13 +826,15 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat this.breakDuration = (value as Double? ?: 0.0).toLong() * 60 * 1000 } SessionRow.BUY_IN -> { - val localResult = if (this.result != null) this.result as Result else realm.createObject(Result::class.java) + val localResult = + if (this.result != null) this.result as Result else realm.createObject(Result::class.java) localResult.buyin = value as Double? this.result = localResult this.updateRowRepresentation() } SessionRow.CASHED_OUT, SessionRow.PRIZE -> { - val localResult = if (this.result != null) this.result as Result else realm.createObject(Result::class.java) + val localResult = + if (this.result != null) this.result as Result else realm.createObject(Result::class.java) localResult.cashout = value as Double? @@ -870,7 +916,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat // Stat Base - override fun formattedValue(stat: Stat, context: Context) : TextFormat { + override val entryTitle: String + get() { + return DateFormat.getDateInstance(DateFormat.SHORT).format(this.startDate) + } + + override fun formattedValue(stat: Stat, context: Context): TextFormat { this.result?.let { result -> @@ -886,7 +937,10 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } } Stat.HOURLY_RATE_BB -> this.bbHourlyRate - Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> Stat.netBBPer100Hands(this.bbNet, this.estimatedHands) + Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> Stat.netBBPer100Hands( + this.bbNet, + this.estimatedHands + ) Stat.AVERAGE_NET_BB -> this.bbNet Stat.DURATION, Stat.AVERAGE_DURATION -> this.netDuration.toDouble() Stat.HOURLY_RATE, Stat.STANDARD_DEVIATION_HOURLY -> this.hourlyRate @@ -914,4 +968,3 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } - diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt index 8842e1d7..51a0bc1c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt @@ -15,6 +15,7 @@ import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.interfaces.Timed import net.pokeranalytics.android.util.NULL_TEXT +import java.text.DateFormat import java.util.* @@ -86,25 +87,6 @@ open class SessionSet() : RealmObject(), Timed, Filterable { return this.bbNet / this.hourlyDuration } - override fun formattedValue(stat: Stat, context: Context) : TextFormat { - return when (stat) { - Stat.NETRESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null, context = context) - Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), currency = null, context = context) - Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null, context = context) - Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null, context = context) - Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, currency = null, context = context) - Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> { - val netBBPer100Hands = Stat.netBBPer100Hands(this.bbNet, this.estimatedHands) - if (netBBPer100Hands != null) { - return stat.format(this.estimatedHands, currency = null, context = context) - } else { - return TextFormat(NULL_TEXT) - } - } - else -> throw StatFormattingException("format undefined for stat ${stat.name}") - } - } - enum class Field(val identifier: String) { RATED_NET("ratedNet"), HOURLY_RATE("hourlyRate"), @@ -126,6 +108,32 @@ open class SessionSet() : RealmObject(), Timed, Filterable { } + // Stat Base + + override val entryTitle: String + get() { + return DateFormat.getDateInstance(DateFormat.SHORT).format(this.startDate) + } + + override fun formattedValue(stat: Stat, context: Context) : TextFormat { + return when (stat) { + Stat.NETRESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null, context = context) + Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), currency = null, context = context) + Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null, context = context) + Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null, context = context) + Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, currency = null, context = context) + Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> { + val netBBPer100Hands = Stat.netBBPer100Hands(this.bbNet, this.estimatedHands) + if (netBBPer100Hands != null) { + return stat.format(this.estimatedHands, currency = null, context = context) + } else { + return TextFormat(NULL_TEXT) + } + } + else -> throw StatFormattingException("format undefined for stat ${stat.name}") + } + } + // Timed override val objectIdentifier: ObjectIdentifier diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt index af6939bb..5c0041e9 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt @@ -75,7 +75,7 @@ class HomeActivity : PokerAnalyticsActivity() { override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.toolbar_home, menu) this.homeMenu = menu - //TODO: Change filter button visibility + //TODO: Change queryWith button visibility homeMenu?.findItem(R.id.filter)?.isVisible = true return super.onCreateOptionsMenu(menu) } @@ -153,15 +153,15 @@ class HomeActivity : PokerAnalyticsActivity() { /* 0 -> { toolbar.title = getString(R.string.feed) - homeMenu?.findItem(R.id.filter)?.isVisible = false + homeMenu?.findItem(R.id.queryWith)?.isVisible = false } 1 -> { toolbar.title = getString(R.string.stats) - homeMenu?.findItem(R.id.filter)?.isVisible = false + homeMenu?.findItem(R.id.queryWith)?.isVisible = false } 2 -> { toolbar.title = getString(R.string.services) - homeMenu?.findItem(R.id.filter)?.isVisible = false + homeMenu?.findItem(R.id.queryWith)?.isVisible = false } */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt index d985b9a5..c6092dd4 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt @@ -151,7 +151,7 @@ class CalendarFragment : SessionObserverFragment(), CoroutineScope, StaticRowRep } }) - // Manage session type filter + // Manage session type queryWith filterSessionAll.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { sessionTypeCondition = null @@ -183,7 +183,7 @@ class CalendarFragment : SessionObserverFragment(), CoroutineScope, StaticRowRep } } - // Manage time filter + // Manage time queryWith filterTimeMonth.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { currentTimeFilter = TimeFilter.MONTH diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt index 40a5f226..fda6e770 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt @@ -243,7 +243,7 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresent */ private fun saveData() { //TODO: Save currentFilter details data - Timber.d("Save data for filter: ${currentFilter?.id}") + Timber.d("Save data for queryWith: ${currentFilter?.id}") selectedRows.forEach { Timber.d("Selected rows: $it") } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt index c470f429..88a45a50 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt @@ -60,7 +60,7 @@ open class FiltersFragment : PokerAnalyticsFragment(), StaticRowRepresentableDat Timber.d("onActivityResult: $requestCode") if (data != null && data.hasExtra(FilterDetailsActivity.IntentKey.FILTER_ID.keyName)) { val filterId = data.getStringExtra(FilterDetailsActivity.IntentKey.FILTER_ID.keyName) - Timber.d("Updated filter: ${filterId}") + Timber.d("Updated queryWith: ${filterId}") } */ @@ -179,19 +179,19 @@ open class FiltersFragment : PokerAnalyticsFragment(), StaticRowRepresentableDat } /** - * Valid the updates of the filter + * Valid the updates of the queryWith */ private fun validUpdates() { - Timber.d("Valid filter updates") + Timber.d("Valid queryWith updates") val filterId = currentFilter?.id ?: "" finishActivityWithResult(filterId) } /** - * Cancel the latest updates of the filter + * Cancel the latest updates of the queryWith */ private fun cancelUpdates() { - Timber.d("Cancel filter updates") + Timber.d("Cancel queryWith updates") val filterId = filterCopy?.id ?: "" @@ -208,7 +208,7 @@ open class FiltersFragment : PokerAnalyticsFragment(), StaticRowRepresentableDat * Delete data */ private fun deleteFilter() { - Timber.d("Delete filter") + Timber.d("Delete queryWith") val realm = getRealm() realm.beginTransaction() currentFilter?.deleteFromRealm() 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 25798bad..9de21a2a 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 @@ -4,19 +4,17 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.view.isVisible import com.github.mikephil.charting.charts.BarChart import com.github.mikephil.charting.charts.BarLineChartBase import com.github.mikephil.charting.charts.LineChart -import com.github.mikephil.charting.data.Entry -import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.* import com.github.mikephil.charting.highlight.Highlight import com.github.mikephil.charting.listener.OnChartValueSelectedListener import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup -import kotlinx.android.synthetic.main.fragment_graph.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import io.realm.Realm +import kotlinx.android.synthetic.main.fragment_evograph.* +import kotlinx.coroutines.* import net.pokeranalytics.android.R import net.pokeranalytics.android.calculus.* import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity @@ -26,7 +24,8 @@ 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 java.text.DateFormat +import timber.log.Timber +import java.util.* import kotlin.coroutines.CoroutineContext class GraphParameters(var stat: Stat, var computableGroup: ComputableGroup, var report: Report) { @@ -52,50 +51,39 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co private var stat: Stat = Stat.NETRESULT private var reports: MutableMap = hashMapOf() + lateinit private var computableGroup: ComputableGroup - private var computableGroup: ComputableGroup? = null - private var selectedReport: Report? = null - private var legendView: LegendView? = null - private var chartView: BarLineChartBase<*>? = null + lateinit private var selectedReport: Report + + lateinit var legendView: LegendView + lateinit var chartView: BarLineChartBase<*> - private var displayAggregationChoices: Boolean = true private var aggregationTypes: List = listOf() override val coroutineContext: CoroutineContext get() = Dispatchers.Main - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_graph, container, false) + companion object { + } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - initUI() + fun setData(stat: Stat, group: ComputableGroup, report: Report) { + this.stat = stat + this.computableGroup = group - selectedReport?.let { - loadGraph(it) - } + this.aggregationTypes = stat.aggregationTypes + this.reports[this.aggregationTypes.first()] = report + this.selectedReport = report } - // OnChartValueSelectedListener - override fun onNothingSelected() { - // nothing to do + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_evograph, container, false) } - override fun onValueSelected(e: Entry?, h: Highlight?) { - e?.let { entry -> - val identifier = entry.data as ObjectIdentifier - val item = getRealm().where(identifier.clazz).equalTo("id", identifier.id).findAll().firstOrNull() - item?.let { - - val formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(it.startDate()) - val entryValue = it.formattedValue(this.stat, requireContext()) - val totalStatValue = this.stat.format(e.y.toDouble(), currency = null, context = requireContext()) - - this.legendView?.setItemData(this.stat, formattedDate, entryValue, totalStatValue) - } - } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initUI() } /** @@ -109,6 +97,16 @@ 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) + } + + this.chartView.setStyle(false, requireContext()) + this.chartContainer.addView(this.chartView) + + this.loadGraph(this.aggregationTypes.first(), this.selectedReport) + this.aggregationTypes.forEach { type -> val chip = Chip(requireContext()) chip.id = type.ordinal @@ -118,7 +116,6 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co this.chipGroup.addView(chip) } - this.chipGroup.isVisible = displayAggregationChoices this.chipGroup.check(this.stat.aggregationTypes.first().ordinal) this.chipGroup.setOnCheckedChangeListener(object : ChipGroupExtension.SingleSelectionOnCheckedListener() { @@ -127,9 +124,9 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co val aggregationType = aggregationTypes[checkedId] reports[aggregationType]?.let { - loadGraph(it) + loadGraph(aggregationType, it) } ?: run { - + launchStatComputation(aggregationType) } } @@ -137,8 +134,7 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co } - /* - private fun launchStatComputation() { + private fun launchStatComputation(aggregationType: AggregationType) { GlobalScope.launch(coroutineContext) { @@ -149,8 +145,10 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co val realm = Realm.getDefaultInstance() - val aggregationType = stat.aggregationTypes.first() - r = Calculator.computeStatsWithEvolutionByAggregationType(realm, computableGroup, aggregationType) + val report = Calculator.computeStatsWithEvolutionByAggregationType(realm, computableGroup, aggregationType) + reports[aggregationType] = report + + r = report realm.close() @@ -163,61 +161,84 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co if (!isDetached) { r?.let { - loadGraph(it) + loadGraph(aggregationType, it) } } } + } - */ - /** - * Load graph - */ - private fun loadGraph(report: Report) { + fun loadGraph(aggregationType: AggregationType, report: Report) { + + val graphEntries = when (aggregationType) { + AggregationType.SESSION, AggregationType.DURATION -> report.results.firstOrNull()?.defaultStatEntries(stat) + AggregationType.MONTH, AggregationType.YEAR -> report.lineEntries(this.stat) + } - report.results.firstOrNull()?.defaultStatEntries(stat)?.let { entries -> - - this.legendView?.prepareWithStat(this.stat, entries.size) + graphEntries?.let { entries -> - val dataSet = PALineDataSet(entries, this.stat.name, requireContext()) - val colors = arrayOf(R.color.green_light).toIntArray() - dataSet.setColors(colors, context) - dataSet.setDrawCircles(false) - dataSet.setDrawValues(false) - val lineData = LineData(listOf(dataSet)) + this.legendView.prepareWithStat(this.stat, entries.size) - this.chartView = when (stat.graphType) { + when (stat.graphType) { GraphType.LINE -> { - val lineChart = LineChart(context) + val lineChart: LineChart = this.chartView as LineChart + + val dataSet = PALineDataSet(entries, this.stat.name, requireContext()) + val colors = arrayOf(R.color.green_light).toIntArray() + dataSet.setColors(colors, context) + dataSet.setDrawCircles(false) + val lineData = LineData(listOf(dataSet)) + lineChart.data = lineData - lineChart } GraphType.BAR -> { - val barChart = BarChart(context) - barChart + 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)) + barChart.data = barData } } - this.chartContainer.addView(this.chartView) + this.chartView.setStyle(false, requireContext()) + this.chartView.setOnChartValueSelectedListener(this) - this.chartView?.setStyle(false, requireContext()) - this.chartView?.setOnChartValueSelectedListener(this) - this.chartView?.highlightValue((entries.size - 1).toFloat(), 0) + this.chartView.highlightValue((entries.size - 1).toFloat(), 0) } } - /** - * Set data - */ - fun setData(stat: Stat, group: ComputableGroup, report: Report, displayAggregationChoices: Boolean) { - this.stat = stat - this.computableGroup = group + // 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 { + + val formattedDate = it.entryTitle + val entryValue = it.formattedValue(this.stat, requireContext()) + val totalStatValue = this.stat.format(e.y.toDouble(), currency = null, context = requireContext()) + + this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue) + } + + } - this.aggregationTypes = stat.aggregationTypes - this.reports[this.aggregationTypes.first()] = report - this.selectedReport = report - this.displayAggregationChoices = displayAggregationChoices } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt index 4e66f0ec..2f302352 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt @@ -223,7 +223,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc if (row is StatRow && row.stat.hasEvolutionGraph) { - // filter groups + // queryWith groups val groupResults = this.report?.results?.filter { it.group.name == row.groupName } 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 df878d24..71e7068a 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 @@ -9,7 +9,15 @@ class PALineDataSet(yVals: List, label: String, context: Context) : LineD init { this.highLightColor = context.getColor(R.color.chart_highlight_indicator) + this.setDrawValues(false) } +} -} \ No newline at end of file +//class PABarDataSet(yVals: List, label: String, context: Context) : BarDataSet(yVals, label) { +// +// init { +// this.highLightColor = context.getColor(R.color.chart_highlight_indicator) +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt index deefb062..68aa648f 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphExtensions.kt @@ -45,9 +45,9 @@ fun BarLineChartBase<*>.setStyle(small: Boolean, context: Context) { this.axisRight.isEnabled = false this.legend.isEnabled = false - this.data.isHighlightEnabled = !small this.description.isEnabled = false + this.data?.isHighlightEnabled = !small // @todo // if timeYAxis { diff --git a/app/src/main/res/layout/fragment_graph.xml b/app/src/main/res/layout/fragment_evograph.xml similarity index 100% rename from app/src/main/res/layout/fragment_graph.xml rename to app/src/main/res/layout/fragment_evograph.xml