Refactoring and stats performance

feature/top10
Laurent 7 years ago
parent 5ccbc8961c
commit ea5699f468
  1. 4
      app/src/androidTest/java/net/pokeranalytics/android/BankrollInstrumentedUnitTest.kt
  2. 14
      app/src/androidTest/java/net/pokeranalytics/android/StatsInstrumentedUnitTest.kt
  3. 64
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  4. 52
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  5. 12
      app/src/main/java/net/pokeranalytics/android/calculus/ComputableGroup.kt
  6. 4
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt
  7. 17
      app/src/main/java/net/pokeranalytics/android/calculus/interfaces/Computable.kt
  8. 2
      app/src/main/java/net/pokeranalytics/android/calculus/interfaces/Datable.kt
  9. 2
      app/src/main/java/net/pokeranalytics/android/model/filter/FilterComponent.kt
  10. 51
      app/src/main/java/net/pokeranalytics/android/model/realm/ComputableResult.kt
  11. 19
      app/src/main/java/net/pokeranalytics/android/model/realm/Currency.kt
  12. 49
      app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt
  13. 42
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  14. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  15. 54
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt
  16. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/SessionObserverFragment.kt
  17. 4
      app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt

@ -3,7 +3,7 @@ package net.pokeranalytics.android
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import net.pokeranalytics.android.calculus.Calculator import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputedResults import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.SessionGroup import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Currency import net.pokeranalytics.android.model.realm.Currency
@ -56,7 +56,7 @@ class BankrollInstrumentedUnitTest : RealmInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll() val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions) val group = ComputableGroup(name = "test", computables = sessions)
val options = Calculator.Options() val options = Calculator.Options()

@ -4,7 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.realm.RealmResults import io.realm.RealmResults
import net.pokeranalytics.android.calculus.Calculator import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputedResults import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.SessionGroup import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
@ -77,7 +77,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll() val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions) val group = ComputableGroup(name = "test", computables = sessions)
val options = Calculator.Options() val options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION) options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
@ -227,7 +227,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll() val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions) val group = ComputableGroup(name = "test", computables = sessions)
val options = Calculator.Options() val options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION) options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
@ -294,7 +294,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll() val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions) val group = ComputableGroup(name = "test", computables = sessions)
val options = Calculator.Options() val options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION) options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
@ -377,7 +377,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
realm.commitTransaction() realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll() val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions) val group = ComputableGroup(name = "test", computables = sessions)
val options = Calculator.Options() val options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION) options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
@ -396,7 +396,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
s1.deleteFromRealm() s1.deleteFromRealm()
} }
val group2 = SessionGroup(name = "test", sessions = sessions) val group2 = ComputableGroup(name = "test", computables = sessions)
val results2: ComputedResults = Calculator.compute(group2, options) val results2: ComputedResults = Calculator.compute(group2, options)
val duration2 = results2.computedStat(Stat.DURATION) val duration2 = results2.computedStat(Stat.DURATION)
@ -548,7 +548,7 @@ class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() {
} }
val sessions = realm.where(Session::class.java).findAll() val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions) val group = ComputableGroup(name = "test", computables = sessions)
val options = Calculator.Options() val options = Calculator.Options()
// options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION) // options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)

@ -70,6 +70,64 @@ class PokerAnalyticsApplication : Application() {
} }
} }
//
// private fun createFakeStats() {
//
// val buyinList = arrayListOf(100.0, 200.0, 300.0, 500.0, 1000.0, 2000.0)
// val resultsList = arrayListOf(
// -2500.0, -2000.0, -1500.0, -1000.0, -500.0, 200.0, 1000.0, 1500.0, 2500.0
// )
//
// val commitFrequency = 100
//
// Thread() {
//
// try {
//
// val realm = Realm.getDefaultInstance()
//
// // Test endedSessions
// val pstats = realm.where<ComputableResult>().findAll()
// if (pstats.size < 10) {
//
// val numberOfSessions = 2000
// Timber.d("*** Start creating ${numberOfSessions} fake computables...")
//
// val s = Date()
//
// realm.beginTransaction()
//
// for (index in 0..numberOfSessions) {
//
// if (index % commitFrequency == 0) {
// Timber.d("****** committing at ${index} computables...")
// realm.commitTransaction()
// realm.beginTransaction()
// }
//
// val ps = realm.createObject(ComputableResult::class.java)
// ps.ratedBuyin = buyinList.random()
// ps.ratedNet = resultsList.random()
//
// }
//
// realm.commitTransaction()
//
// val e = Date()
// val duration = (e.time - s.time) / 1000.0
// Timber.d("*** ended in ${duration} seconds")
//
// }
//
// realm.close()
//
// } catch (e: Exception) {
// Timber.e(e)
// }
//
// }.start()
//
// }
private fun createFakeSessions() { private fun createFakeSessions() {
@ -92,8 +150,8 @@ class PokerAnalyticsApplication : Application() {
val sessions = realm.where<Session>().findAll() val sessions = realm.where<Session>().findAll()
if (sessions.size < 10) { if (sessions.size < 10) {
val numberOfSessions = 200 val numberOfSessions = 2000
Timber.d("*** Start creating ${numberOfSessions} fake sessions...") Timber.d("*** Start creating ${numberOfSessions} fake computables...")
val s = Date() val s = Date()
@ -102,7 +160,7 @@ class PokerAnalyticsApplication : Application() {
for (index in 0..numberOfSessions) { for (index in 0..numberOfSessions) {
if (index % commitFrequency == 0) { if (index % commitFrequency == 0) {
Timber.d("****** committing at ${index} sessions...") Timber.d("****** committing at ${index} computables...")
realm.commitTransaction() realm.commitTransaction()
realm.beginTransaction() realm.beginTransaction()
} }

@ -1,7 +1,7 @@
package net.pokeranalytics.android.calculus package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.calculus.Stat.* import net.pokeranalytics.android.calculus.Stat.*
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.calculus.interfaces.Computable
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -66,7 +66,7 @@ class Calculator {
/** /**
* Computes all stats for list of Session sessionGroup * Computes all stats for list of Session sessionGroup
*/ */
fun computeGroups(groups: List<SessionGroup>, options: Options): List<ComputedResults> { fun computeGroups(groups: List<ComputableGroup>, options: Options): List<ComputedResults> {
val computedResults = mutableListOf<ComputedResults>() val computedResults = mutableListOf<ComputedResults>()
groups.forEach { group -> groups.forEach { group ->
@ -75,7 +75,7 @@ class Calculator {
val results: ComputedResults = Calculator.compute(group, options = options) val results: ComputedResults = Calculator.compute(group, options = options)
// Computes the compared sessionGroup if existing // Computes the compared sessionGroup if existing
val comparedGroup = group.comparedSessions val comparedGroup = group.comparedComputables
if (comparedGroup != null) { if (comparedGroup != null) {
val comparedResults = Calculator.compute(comparedGroup, options = options) val comparedResults = Calculator.compute(comparedGroup, options = options)
group.comparedComputedResults = comparedResults group.comparedComputedResults = comparedResults
@ -94,13 +94,13 @@ class Calculator {
return computedResults return computedResults
} }
// fun compute(sessionGroup: SessionGroup, options: Options): ComputedResults { // fun compute(sessionGroup: ComputableGroup, options: Options): ComputedResults {
// //
// var sum: Double = 0.0 // var sum: Double = 0.0
// val sessions: List<SessionInterface> = sessionGroup.sessions // val computables: List<SessionInterface> = sessionGroup.computables
// val results: ComputedResults = ComputedResults(sessionGroup) // val results: ComputedResults = ComputedResults(sessionGroup)
// //
// sessions.forEach { s -> // computables.forEach { s ->
// sum += s.ratedNet // sum += s.ratedNet
// } // }
// //
@ -117,34 +117,34 @@ class Calculator {
/** /**
* Computes stats for a SessionSet * Computes stats for a SessionSet
*/ */
fun compute(sessionGroup: SessionGroup, options: Options): ComputedResults { fun compute(computableGroup: ComputableGroup, options: Options): ComputedResults {
Timber.d(">>>> Start computing group ${sessionGroup.name}, ${sessionGroup.sessions.size} sessions") Timber.d(">>>> Start computing group ${computableGroup.name}, ${computableGroup.computables.size} computables")
val sessions: List<Session> = sessionGroup.sessions val computables: List<Computable> = computableGroup.computables
val sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet() val sessionSets = computableGroup.computables.mapNotNull { it.sessionSet }.toHashSet()
val results: ComputedResults = ComputedResults(sessionGroup) val results: ComputedResults = ComputedResults(computableGroup)
var sum: Double = 0.0 var sum: Double = 0.0
var totalHands: Double = 0.0 var totalHands: Double = 0.0
var bbSum: Double = 0.0 var bbSum: Double = 0.0
var bbSessionCount: Int = 0 var bbSessionCount: Int = 0
var winningSessionCount: Int = 0 // sessions.filter { it.value >= 0.0 }.size var winningSessionCount: Int = 0 // computables.filter { it.value >= 0.0 }.size
var totalBuyin = 0.0 var totalBuyin = 0.0
// Compute for each session // Compute for each session
var index: Int = 0 var index: Int = 0
sessions.forEach { s -> computables.forEach { s ->
index++; index++;
val result = s.result!! // ok to crash to see problems // val result = s.result!! // ok to crash to see problems
sum += result.ratedNet sum += s.ratedNet
bbSum += s.bbNetResult bbSum += s.bbNetResult
bbSessionCount += s.hasBigBlind bbSessionCount += s.hasBigBlind
winningSessionCount = result.isPositive winningSessionCount += s.isPositive
totalBuyin += s.buyin totalBuyin += s.ratedBuyin
totalHands += s.estimatedHands totalHands += s.estimatedHands
when (options.evolutionValues) { when (options.evolutionValues) {
@ -212,10 +212,10 @@ class Calculator {
} }
var average = 0.0 var average = 0.0
if (sessions.size > 0) { if (computables.size > 0) {
average = sum / sessions.size.toDouble() average = sum / computables.size.toDouble()
val winRatio = winningSessionCount.toDouble() / sessions.size.toDouble() val winRatio = winningSessionCount.toDouble() / computables.size.toDouble()
val avgBuyin = totalBuyin / sessions.size val avgBuyin = totalBuyin / computables.size
results.addStats( results.addStats(
setOf( setOf(
@ -242,7 +242,7 @@ class Calculator {
ComputedStat(NETRESULT, sum), ComputedStat(NETRESULT, sum),
ComputedStat(DURATION, duration), ComputedStat(DURATION, duration),
ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()), ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()),
ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()), ComputedStat(NUMBER_OF_GAMES, computables.size.toDouble()),
ComputedStat(HOURLY_RATE_BB, bbSum / duration), ComputedStat(HOURLY_RATE_BB, bbSum / duration),
ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount), ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount),
ComputedStat(HANDS_PLAYED, totalHands) ComputedStat(HANDS_PLAYED, totalHands)
@ -268,12 +268,12 @@ class Calculator {
// Session // Session
var stdSum: Double = 0.0 var stdSum: Double = 0.0
var stdBBper100HandsSum: Double = 0.0 var stdBBper100HandsSum: Double = 0.0
sessions.forEach { session -> computables.forEach { session ->
stdSum += Math.pow(session.result!!.ratedNet - average, 2.0) stdSum += Math.pow(session.ratedNet - average, 2.0)
stdBBper100HandsSum += Math.pow(session.bbPer100Hands - bbPer100Hands, 2.0) stdBBper100HandsSum += Math.pow(session.bbPer100Hands - bbPer100Hands, 2.0)
} }
val standardDeviation: Double = Math.sqrt(stdSum / sessions.size) val standardDeviation: Double = Math.sqrt(stdSum / computables.size)
val standardDeviationBBper100Hands: Double = Math.sqrt(stdBBper100HandsSum / sessions.size) val standardDeviationBBper100Hands: Double = Math.sqrt(stdBBper100HandsSum / computables.size)
// Session Set // Session Set
var hourlyStdSum: Double = 0.0 var hourlyStdSum: Double = 0.0

@ -1,11 +1,11 @@
package net.pokeranalytics.android.calculus package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.calculus.interfaces.Computable
/** /**
* A sessionGroup of computable items identified by a name * A sessionGroup of computable items identified by a name
*/ */
class SessionGroup(name: String, sessions: List<Session>, stats: List<Stat>? = null) { class ComputableGroup(name: String, computables: List<Computable>, stats: List<Stat>? = null) {
/** /**
* The display name of the group * The display name of the group
@ -15,7 +15,7 @@ class SessionGroup(name: String, sessions: List<Session>, stats: List<Stat>? = n
/** /**
* The list of endedSessions to compute * The list of endedSessions to compute
*/ */
var sessions: List<Session> = sessions var computables: List<Computable> = computables
/** /**
* The list of stats to display * The list of stats to display
@ -25,7 +25,7 @@ class SessionGroup(name: String, sessions: List<Session>, stats: List<Stat>? = n
/** /**
* A subgroup used to compute stat variation * A subgroup used to compute stat variation
*/ */
var comparedSessions: SessionGroup? = null var comparedComputables: ComputableGroup? = null
/** /**
* The computed stats of the comparable sessionGroup * The computed stats of the comparable sessionGroup
@ -34,12 +34,12 @@ class SessionGroup(name: String, sessions: List<Session>, stats: List<Stat>? = n
} }
class ComputedResults(group: SessionGroup) { class ComputedResults(group: ComputableGroup) {
/** /**
* The session group used to computed the stats * The session group used to computed the stats
*/ */
var group: SessionGroup = group var group: ComputableGroup = group
// The computed stats of the sessionGroup // The computed stats of the sessionGroup
private var _computedStats: MutableMap<Stat, ComputedStat> = mutableMapOf() private var _computedStats: MutableMap<Stat, ComputedStat> = mutableMapOf()

@ -1,6 +1,6 @@
package net.pokeranalytics.android.calculus.bankroll package net.pokeranalytics.android.calculus.bankroll
import net.pokeranalytics.android.calculus.`interface`.DatableValue import net.pokeranalytics.android.calculus.interfaces.DatableValue
import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import java.util.* import java.util.*
@ -73,7 +73,7 @@ class BankrollReport(setup: BankrollReportSetup) {
private set private set
/** /**
* The net result from poker sessions * The net result from poker computables
*/ */
var netResult: Double = 0.0 var netResult: Double = 0.0
private set private set

@ -0,0 +1,17 @@
package net.pokeranalytics.android.calculus.interfaces
import net.pokeranalytics.android.model.realm.SessionSet
interface Computable {
var ratedNet: Double
var bbNetResult: Double
var hasBigBlind: Int
var isPositive: Int
var ratedBuyin: Double
var estimatedHands: Double
var bbPer100Hands: Double
var sessionSet: SessionSet?
}

@ -1,4 +1,4 @@
package net.pokeranalytics.android.calculus.`interface` package net.pokeranalytics.android.calculus.interfaces
import java.util.* import java.util.*

@ -27,7 +27,7 @@ enum class SessionFilterable(var fieldName:String? = null) : Filterable {
TOURNAMENT_FEATURES("tournamentFeature.id"), TOURNAMENT_FEATURES("tournamentFeature.id"),
TOURNAMENT_NUMBER_OF_PLAYER("tournamentNumberOfPlayers"), TOURNAMENT_NUMBER_OF_PLAYER("tournamentNumberOfPlayers"),
TOURNAMENT_ENTRY_FEE("tournamentEntryFee"), TOURNAMENT_ENTRY_FEE("tournamentEntryFee"),
RESULT_BUYIN("result.buyin"), RESULT_BUYIN("result.ratedBuyin"),
RESULT_CASHED_OUT("result.cashout"), RESULT_CASHED_OUT("result.cashout"),
RESULT_NET("result.ratedNet"), RESULT_NET("result.ratedNet"),
RESULT_TIPS("result.tips"), RESULT_TIPS("result.tips"),

@ -0,0 +1,51 @@
package net.pokeranalytics.android.model.realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects
import net.pokeranalytics.android.calculus.interfaces.Computable
open class ComputableResult : RealmObject(), Computable {
override var ratedNet: Double = 0.0
override var bbNetResult: Double = 0.0
override var hasBigBlind: Int = 0
override var isPositive: Int = 0
override var ratedBuyin: Double = 0.0
override var estimatedHands: Double = 0.0
override var bbPer100Hands: Double = 0.0
override var sessionSet: SessionSet? = null
@LinkingObjects("computableResult")
private val sessions: RealmResults<Session>? = null
@Ignore
val session: Session? = this.sessions?.firstOrNull()
fun updateWith(session: Session) {
val rate = session.bankroll?.currency?.rate ?: 1.0
session.result?.let { result ->
this.ratedNet = result.net * rate
this.isPositive = result.isPositive
this.ratedBuyin = (result.buyin ?: 0.0) * rate
}
this.bbNetResult = session.bbNetResult
this.hasBigBlind = session.hasBigBlind
this.estimatedHands = session.estimatedHands
this.bbPer100Hands = session.bbPer100Hands
this.sessionSet = session.sessionSet
}
}

@ -15,5 +15,24 @@ open class Currency : RealmObject() {
// The rate of the currency with the main currency // The rate of the currency with the main currency
var rate: Double? = null var rate: Double? = null
set(value) {
field = value
val rate = value ?: 1.0
// could be async
val cResults = this.realm.where(ComputableResult::class.java).equalTo("session.bankroll.currency", this.id).findAll()
cResults.forEach { computable ->
computable.session?.result?.net?.let {
computable.ratedNet = it * rate
}
computable.session?.result?.buyin?.let {
computable.ratedBuyin = it * rate
}
}
}
} }

@ -2,29 +2,35 @@ package net.pokeranalytics.android.model.realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects
import io.realm.annotations.RealmClass import io.realm.annotations.RealmClass
@RealmClass @RealmClass
open class Result : RealmObject() { open class Result : RealmObject() {
// The buyin amount /**
* The buyin amount
*/
var buyin: Double? = null var buyin: Double? = null
set(value) { set(value) {
field = value field = value
this.computeNet() this.computeNet()
} }
// the cashed out amount /**
* The cashed out amount
*/
var cashout: Double? = null var cashout: Double? = null
set(value) { set(value) {
field = value field = value
this.computeNet() this.computeNet()
} }
// Tips /**
var tips: Double? = null * The net result
*/
// The net result
var netResult: Double? = null var netResult: Double? = null
set(value) { set(value) {
field = value field = value
@ -38,10 +44,9 @@ open class Result : RealmObject() {
private set private set
/** /**
* The pre-computed rated net (readonly) * Tips
*/ */
var ratedNet: Double = 0.0 var tips: Double? = null
private set
// The transactions associated with the Result, impacting the result // The transactions associated with the Result, impacting the result
var transactions: RealmList<Transaction> = RealmList() var transactions: RealmList<Transaction> = RealmList()
@ -53,23 +58,17 @@ open class Result : RealmObject() {
// The tournament final position, if applicable // The tournament final position, if applicable
var tournamentFinalPosition: Int? = null var tournamentFinalPosition: Int? = null
/** @LinkingObjects("result")
* The automatically Realm-updated sessions list private val sessions: RealmResults<Session>? = null
* Should contain only one element
*/
// @LinkingObjects("result")
// private val sessions: RealmResults<Session>? = null
/** @Ignore
* The associated Session val session: Session? = this.sessions?.firstOrNull()
*/
// @Ignore
// val session: Session? = this.sessions?.firstOrNull()
/** /**
* Returns 1 if the session is positive * Returns 1 if the session is positive
*/ */
var isPositive: Int = 0 @Ignore
val isPositive: Int = if (this.net >= 0.0) 1 else 0
// Computes the Net // Computes the Net
private fun computeNet() { private fun computeNet() {
@ -81,12 +80,12 @@ open class Result : RealmObject() {
val buyin = this.buyin ?: 0.0 val buyin = this.buyin ?: 0.0
val cashOut = this.cashout ?: 0.0 val cashOut = this.cashout ?: 0.0
this.net = cashOut - buyin + transactionsSum this.net = cashOut - buyin + transactionsSum
}
this.ratedNet = this.net * 1.0
this.isPositive = if (this.ratedNet >= 0.0) 1 else 0
} }
// private var rate: Double = (this.session?.bankroll?.currency?.rate ?: 1.0) // Update ComputableResult
this.session?.updateComputableResult()
}
// @todo tips? // @todo tips?
} }

@ -5,6 +5,7 @@ import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.Index
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
@ -36,6 +37,18 @@ import kotlin.collections.ArrayList
open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, Timed { open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, Timed {
// @Ignore
// var rate: Double = 0.0
// get() = this.bankroll?.currency?.rate ?: 0.0
//
// @Ignore
// override var ratedNet: Double = 0.0
// get() = this.result!!.net * this.rate
//
// @Ignore
// override var isPositive: Int = 0
// get() = this.result!!.isPositive
enum class Type { enum class Type {
CASH_GAME, CASH_GAME,
TOURNAMENT TOURNAMENT
@ -45,6 +58,7 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null): Session { fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null): Session {
val session = Session() val session = Session()
session.result = Result() session.result = Result()
session.computableResult = ComputableResult()
if (bankroll != null) { if (bankroll != null) {
session.bankroll = bankroll session.bankroll = bankroll
} else { } else {
@ -66,6 +80,11 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
// The result of the main user // The result of the main user
var result: Result? = null var result: Result? = null
/**
* Optimized result for faster stats
*/
var computableResult: ComputableResult? = null
// Timed interface // Timed interface
/** /**
@ -75,23 +94,24 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
set(value) { set(value) {
field = value field = value
this.computeNetDuration() this.computeNetDuration()
this.computeStats()
// nullifies endate when setting the start date after the end date // nullifies endate when setting the start date after the end date
if (value != null && this.endDate != null && value.after(this.endDate)) { if (value != null && this.endDate != null && value.after(this.endDate)) {
this.endDate = null this.endDate = null
} }
this.dateChanged() this.dateChanged()
this.computeStats()
} }
/** /**
* the end date of the session * the end date of the session
*/ */
@Index
var endDate: Date? = null var endDate: Date? = null
set(value) { set(value) {
field = value field = value
this.computeNetDuration() this.computeNetDuration()
this.computeStats()
this.dateChanged() this.dateChanged()
this.computeStats()
} }
/** /**
@ -166,6 +186,7 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
set(value) { set(value) {
this.hasBigBlind = if (value != null) 1 else 0 this.hasBigBlind = if (value != null) 1 else 0
field = value field = value
this.updateComputableResult()
} }
// Tournament // Tournament
@ -195,6 +216,7 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
} else if (this.sessionSet != null) { } else if (this.sessionSet != null) {
SessionSetManager.removeFromTimeline(this) SessionSetManager.removeFromTimeline(this)
} }
this.updateComputableResult()
this.updateRowRepresentation() this.updateRowRepresentation()
} }
@ -258,6 +280,15 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
this.computeEstimatedHands() this.computeEstimatedHands()
this.computeBBNetResult() this.computeBBNetResult()
this.computeBBPer100Hands() this.computeBBPer100Hands()
this.updateComputableResult()
}
fun updateComputableResult() {
this.computableResult?.let {
it.updateWith(this)
} ?: run {
throw IllegalStateException("Session should always have a Light Result")
}
} }
/** /**
@ -295,7 +326,7 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
private set private set
@Ignore @Ignore
var buyin: Double = 0.0 var ratedBuyin: Double = 0.0
get() { get() {
this.result?.let { result -> this.result?.let { result ->
result.buyin?.let { result.buyin?.let {
@ -472,7 +503,7 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
} }
@Ignore @Ignore
private var rowRepresentationForCurrentState : List<RowRepresentable> = this.updatedRowRepresentationForCurrentState() private var rowRepresentationForCurrentState : List<RowRepresentable> = mutableListOf()
private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> { private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
@ -541,7 +572,6 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return this.rowRepresentationForCurrentState return this.rowRepresentationForCurrentState
} }
override fun boolForRow(row: RowRepresentable): Boolean { override fun boolForRow(row: RowRepresentable): Boolean {
@ -624,7 +654,7 @@ open class Session : RealmObject(), Manageable, StaticRowRepresentableDataSource
SessionRow.BUY_IN -> row.editingDescriptors(mapOf( SessionRow.BUY_IN -> row.editingDescriptors(mapOf(
"bb" to cgBigBlind, "bb" to cgBigBlind,
"fee" to this.tournamentEntryFee, "fee" to this.tournamentEntryFee,
"buyin" to buyin "ratedBuyin" to ratedBuyin
)) ))
SessionRow.BREAK_TIME -> row.editingDescriptors(mapOf()) SessionRow.BREAK_TIME -> row.editingDescriptors(mapOf())
SessionRow.CASHED_OUT, SessionRow.PRIZE, SessionRow.NET_RESULT -> row.editingDescriptors(mapOf( SessionRow.CASHED_OUT, SessionRow.PRIZE, SessionRow.NET_RESULT -> row.editingDescriptors(mapOf(

@ -68,7 +68,7 @@ open class SessionSet : RealmObject(), Timed {
val sessions: RealmResults<Session>? = null val sessions: RealmResults<Session>? = null
@Ignore @Ignore
val ratedNet: Double = this.sessions?.sumByDouble { it.result!!.ratedNet } ?: 0.0 val ratedNet: Double = this.sessions?.sumByDouble { it.computableResult!!.ratedNet } ?: 0.0
@Ignore @Ignore
val hourlyRate: Double = this.ratedNet / this.hourlyDuration val hourlyRate: Double = this.ratedNet / this.hourlyDuration

@ -10,8 +10,9 @@ import kotlinx.android.synthetic.main.fragment_stats.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.* import net.pokeranalytics.android.calculus.*
import net.pokeranalytics.android.calculus.interfaces.Computable
import net.pokeranalytics.android.model.StatRepresentable import net.pokeranalytics.android.model.StatRepresentable
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.ui.adapter.DisplayDescriptor import net.pokeranalytics.android.ui.adapter.DisplayDescriptor
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
@ -92,7 +93,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
// Override // Override
override fun sessionsChanged() { override fun sessionsChanged() {
// this.launchStatComputation() this.launchStatComputation()
this.statsAdapter.notifyDataSetChanged() this.statsAdapter.notifyDataSetChanged()
} }
@ -109,26 +110,6 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
private fun launchStatComputation() { private fun launchStatComputation() {
// Thread() {
//// var results = listOf<ComputedResults>()
// val s = Date()
// Timber.d(">>> start...")
//
// val results = createSessionGroupsAndStartCompute()
//
// val e = Date()
// val duration = (e.time - s.time) / 1000.0
// Timber.d(">>> ended in ${duration} seconds")
//
//
// val mainHandler = Handler(Looper.getMainLooper())
// val runnable = Runnable {
// showResults(results)
// }
// mainHandler.post(runnable)
//
// }.start()
GlobalScope.launch(coroutineContext) { GlobalScope.launch(coroutineContext) {
var results = listOf<ComputedResults>() var results = listOf<ComputedResults>()
@ -151,35 +132,36 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
private fun createSessionGroupsAndStartCompute() : List<ComputedResults> { private fun createSessionGroupsAndStartCompute() : List<ComputedResults> {
val cgSessions = mutableListOf<Session>() val cgSessions = mutableListOf<Computable>()
val tSessions = mutableListOf<Session>() val tSessions = mutableListOf<Computable>()
val s = Date() val s = Date()
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
val allSessions = realm.where(Session::class.java).isNotNull("endDate").findAll() // val allSessions = realm.where(ComputableResult::class.java).findAll()
Timber.d(">>>>> number of sessions to compute = ${allSessions.size}") val allSessions = realm.where(ComputableResult::class.java).isNotNull("sessions.endDate").findAll()
Timber.d(">>>>> number of computables to compute = ${allSessions.size}")
val sessionsList = allSessions.toList() val computableList = realm.copyFromRealm(allSessions)
realm.close()
val e = Date() computableList.forEach { session ->
val duration = (e.time - s.time) / 1000.0 if (true) { // @todo
Timber.d(">>> filtering in ${duration} seconds")
sessionsList.forEach { session ->
if (session.isCashGame()) {
cgSessions.add(session) cgSessions.add(session)
} else { } else {
tSessions.add(session) tSessions.add(session)
} }
} }
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> filtering in ${duration} seconds")
val allStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NUMBER_OF_SETS, Stat.AVERAGE_DURATION, Stat.DURATION) val allStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NUMBER_OF_SETS, Stat.AVERAGE_DURATION, Stat.DURATION)
val allSessionGroup = SessionGroup(getString(R.string.all), sessionsList, allStats) val allSessionGroup = ComputableGroup(getString(R.string.all), computableList, allStats)
val cgStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.NET_BB_PER_100_HANDS, Stat.HOURLY_RATE_BB, Stat.AVERAGE, Stat.STANDARD_DEVIATION_HOURLY, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN) val cgStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.NET_BB_PER_100_HANDS, Stat.HOURLY_RATE_BB, Stat.AVERAGE, Stat.STANDARD_DEVIATION_HOURLY, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN)
val cgSessionGroup = SessionGroup(getString(R.string.cash_game), cgSessions, cgStats) val cgSessionGroup = ComputableGroup(getString(R.string.cash_game), cgSessions, cgStats)
val tStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN) val tStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN)
val tSessionGroup = SessionGroup(getString(R.string.tournament), tSessions, tStats) val tSessionGroup = ComputableGroup(getString(R.string.tournament), tSessions, tStats)

@ -2,15 +2,15 @@ package net.pokeranalytics.android.ui.fragment.components
import io.realm.Realm import io.realm.Realm
import io.realm.RealmResults import io.realm.RealmResults
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.ComputableResult
open class SessionObserverFragment : PokerAnalyticsFragment() { open class SessionObserverFragment : PokerAnalyticsFragment() {
val endedSessions: RealmResults<Session> val endedSessions: RealmResults<ComputableResult>
init { init {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
this.endedSessions = realm.where(Session::class.java).isNotNull("endDate").findAll() this.endedSessions = realm.where(ComputableResult::class.java).isNotNull("sessions.endDate").findAll()
this.endedSessions.addChangeListener { _, _ -> this.endedSessions.addChangeListener { _, _ ->
this.sessionsChanged() this.sessionsChanged()
} }

@ -27,7 +27,7 @@ class ExampleUnitTest : RealmUnitTest() {
// override var estimatedHands: Double = 0.0 // override var estimatedHands: Double = 0.0
// override var bbNetResult: Double = 0.0 // override var bbNetResult: Double = 0.0
// override var bigBlindSessionCount: Int = 0 // 0 or 1 // override var bigBlindSessionCount: Int = 0 // 0 or 1
// override var buyin: Double = 0.0 // override var ratedBuyin: Double = 0.0
// //
// } // }
// //
@ -35,7 +35,7 @@ class ExampleUnitTest : RealmUnitTest() {
// fun testStats() { // fun testStats() {
// //
// val grades: List<Grade> = listOf(Grade(10.0), Grade(20.0)) // val grades: List<Grade> = listOf(Grade(10.0), Grade(20.0))
// val group = SessionGroup(name = "test", sessions = grades) // val group = ComputableGroup(name = "test", computables = grades)
// //
// val results: ComputedResults = Calculator.compute(group, Calculator.Options()) // val results: ComputedResults = Calculator.compute(group, Calculator.Options())
// //

Loading…
Cancel
Save