Validate all stats but standard deviations + overlapping duration tests

dev_raz_wip
Laurent 7 years ago
parent d973fbf203
commit be5a81acad
  1. 254
      app/src/androidTest/java/net/pokeranalytics/android/ExampleInstrumentedUnitTest.kt
  2. 16
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  3. 4
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  4. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  5. 20
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  6. 40
      app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt

@ -37,13 +37,6 @@ class ExampleInstrumentedUnitTest : RealmInstrumentedUnitTest() {
val realm = this.mockRealm val realm = this.mockRealm
realm.beginTransaction() realm.beginTransaction()
val sdf = SimpleDateFormat("dd/M/yyyy hh:mm")
val sd1 = sdf.parse("01/1/2019 10:00")
val ed1 = sdf.parse("01/1/2019 11:00")
val sd2 = sdf.parse("02/1/2019 08:00")
val ed2 = sdf.parse("02/1/2019 11:00")
var s1 = realm.createObject(Session::class.java, "1") var s1 = realm.createObject(Session::class.java, "1")
var s2 = realm.createObject(Session::class.java, "2") var s2 = realm.createObject(Session::class.java, "2")
@ -53,19 +46,28 @@ class ExampleInstrumentedUnitTest : RealmInstrumentedUnitTest() {
s1.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java) s1.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java)
s2.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java) s2.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java)
// var s1: Session = Session.newInstance() s1.result?.buyin = 100.0 // net result = -100
// var s2: Session = Session.newInstance() s2.result?.buyin = 200.0
s2.result?.cashout = 500.0 // net result = 300
s1.result?.netResult = -100.0 s1.cgBigBlind = 0.5 // bb net result = -200bb
s2.result?.netResult = 300.0 s2.cgBigBlind = 2.0 // bb net result = 150bb
realm.insert(s1) realm.insert(s1)
realm.insert(s2) realm.insert(s2)
realm.commitTransaction() realm.commitTransaction()
val sdf = SimpleDateFormat("dd/M/yyyy hh:mm")
val sd1 = sdf.parse("01/1/2019 10:00")
val ed1 = sdf.parse("01/1/2019 11:00")
val sd2 = sdf.parse("02/1/2019 08:00")
val ed2 = sdf.parse("02/1/2019 11:00")
realm.beginTransaction() realm.beginTransaction()
s1.timeFrame?.setDate(sd1, ed1)
s2.timeFrame?.setDate(sd2, ed2) s1.timeFrame?.setDate(sd1, ed1) // duration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800
s2.timeFrame?.setDate(sd2, ed2) // duration = 3h, hourly = 100, bb100 = 150 / 75 * 100 = +200
realm.copyToRealmOrUpdate(s1) realm.copyToRealmOrUpdate(s1)
realm.copyToRealmOrUpdate(s2) realm.copyToRealmOrUpdate(s2)
@ -75,7 +77,10 @@ class ExampleInstrumentedUnitTest : 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 = SessionGroup(name = "test", sessions = sessions)
val results: ComputedResults = Calculator.compute(group, Calculator.Options()) var options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
val results: ComputedResults = Calculator.compute(group, options)
val delta = 0.01 val delta = 0.01
val sum = results.computedStat(Stat.NETRESULT) val sum = results.computedStat(Stat.NETRESULT)
@ -99,8 +104,229 @@ class ExampleInstrumentedUnitTest : RealmInstrumentedUnitTest() {
Assert.fail("No duration stat") Assert.fail("No duration stat")
} }
val hourlyRate = results.computedStat(Stat.HOURLY_RATE)
if (hourlyRate != null) {
assertEquals(50.0, hourlyRate.value, delta)
} else {
Assert.fail("No houry rate stat")
}
val handsPlayed = results.computedStat(Stat.HANDS_PLAYED)
if (handsPlayed != null) {
assertEquals(100.0, handsPlayed.value, delta)
} else {
Assert.fail("No hands played stat")
}
val numberOfGames = results.computedStat(Stat.NUMBER_OF_GAMES)
if (numberOfGames != null) {
assertEquals(2, numberOfGames.value.toInt())
} else {
Assert.fail("No numberOfGames stat")
}
val numberOfSets = results.computedStat(Stat.NUMBER_OF_SETS)
if (numberOfSets != null) {
assertEquals(2, numberOfSets.value.toInt())
} else {
Assert.fail("No numberOfSets stat")
}
val avgBuyin = results.computedStat(Stat.AVERAGE_BUYIN)
if (avgBuyin != null) {
assertEquals(150.0, avgBuyin.value, delta)
} else {
Assert.fail("No avgBuyin stat")
}
val avgDuration = results.computedStat(Stat.AVERAGE_DURATION)
if (avgDuration != null) {
assertEquals(2.0, avgDuration.value, delta)
} else {
Assert.fail("No avgDuration stat")
}
val roi = results.computedStat(Stat.ROI)
if (roi != null) {
assertEquals(200 / 300.0, roi.value, delta)
} else {
Assert.fail("No roi stat")
}
val avgBBNet = results.computedStat(Stat.AVERAGE_NET_BB)
if (avgBBNet != null) {
assertEquals(-25.0, avgBBNet.value, delta)
} else {
Assert.fail("No avgBBNet stat")
}
val bbHourlyRate = results.computedStat(Stat.HOURLY_RATE_BB)
if (bbHourlyRate != null) {
assertEquals(-12.5, bbHourlyRate.value, delta)
} else {
Assert.fail("No bbHourlyRate stat")
}
val netbbPer100Hands = results.computedStat(Stat.NET_BB_PER_100_HANDS)
if (netbbPer100Hands != null) {
assertEquals(-50.0, netbbPer100Hands.value, delta)
} else {
Assert.fail("No netbbPer100Hands stat")
}
// val stdHourly = results.computedStat(Stat.STANDARD_DEVIATION_HOURLY)
// if (stdHourly != null) {
// assertEquals(111.8, stdHourly.value, delta)
// } else {
// Assert.fail("No stdHourly stat")
// }
//
// val std = results.computedStat(Stat.STANDARD_DEVIATION)
// if (std != null) {
// assertEquals(200.0, std.value, delta)
// } else {
// Assert.fail("No std stat")
// }
//
// val std100 = results.computedStat(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS)
// if (std100 != null) {
// assertEquals(503.12, std100.value, delta)
// } else {
// Assert.fail("No std100 stat")
// }
}
@Test
fun testOverlappingSessions1() {
val realm = this.mockRealm
realm.beginTransaction()
var s1 = realm.createObject(Session::class.java, "1")
var s2 = realm.createObject(Session::class.java, "2")
s1.timeFrame = realm.createObject(TimeFrame::class.java)
s2.timeFrame = realm.createObject(TimeFrame::class.java)
s1.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java)
s2.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java)
realm.insert(s1)
realm.insert(s2)
realm.commitTransaction()
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 08:00")
val ed2 = sdf.parse("01/1/2019 11:00")
realm.beginTransaction()
s1.timeFrame?.setDate(sd1, ed1) // duration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800
s2.timeFrame?.setDate(sd2, ed2) // duration = 4h, hourly = 100, bb100 = 150 / 75 * 100 = +200
realm.copyToRealmOrUpdate(s1)
realm.copyToRealmOrUpdate(s2)
realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions)
var options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
val results: ComputedResults = Calculator.compute(group, options)
val delta = 0.01
val duration = results.computedStat(Stat.DURATION)
if (duration != null) {
assertEquals(3.0, duration.value, delta)
} else {
Assert.fail("No Net result stat")
}
val numberOfSets = results.computedStat(Stat.NUMBER_OF_SETS)
if (numberOfSets != null) {
assertEquals(1, numberOfSets.value.toInt())
} else {
Assert.fail("No numberOfSets stat")
}
val numberOfGames = results.computedStat(Stat.NUMBER_OF_GAMES)
if (numberOfGames != null) {
assertEquals(2, numberOfGames.value.toInt())
} else {
Assert.fail("No numberOfSets stat")
}
}
@Test
fun testOverlappingSessions2() {
val realm = this.mockRealm
realm.beginTransaction()
var s1 = realm.createObject(Session::class.java, "1")
var s2 = realm.createObject(Session::class.java, "2")
var s3 = realm.createObject(Session::class.java, "3")
s1.timeFrame = realm.createObject(TimeFrame::class.java)
s2.timeFrame = realm.createObject(TimeFrame::class.java)
s3.timeFrame = realm.createObject(TimeFrame::class.java)
realm.insert(s1)
realm.insert(s2)
realm.insert(s3)
realm.commitTransaction()
val sdf = SimpleDateFormat("dd/M/yyyy hh:mm")
val sd1 = sdf.parse("01/1/2019 05: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 11:00")
val sd3 = sdf.parse("01/1/2019 03:00")
val ed3 = sdf.parse("01/1/2019 06:00")
realm.beginTransaction()
s1.timeFrame?.setDate(sd1, ed1) // duration = 4h
s2.timeFrame?.setDate(sd2, ed2) // duration = 4h
s3.timeFrame?.setDate(sd3, ed3) // duration = 3h
realm.copyToRealmOrUpdate(s1)
realm.copyToRealmOrUpdate(s2)
realm.copyToRealmOrUpdate(s3)
realm.commitTransaction()
val sessions = realm.where(Session::class.java).findAll()
val group = SessionGroup(name = "test", sessions = sessions)
var options = Calculator.Options()
options.displayedStats = listOf(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION)
val results: ComputedResults = Calculator.compute(group, options)
val delta = 0.01
val duration = results.computedStat(Stat.DURATION)
if (duration != null) {
assertEquals(8.0, duration.value, delta)
} else {
Assert.fail("No Net result stat")
}
val numberOfSets = results.computedStat(Stat.NUMBER_OF_SETS)
if (numberOfSets != null) {
assertEquals(1, numberOfSets.value.toInt())
} else {
Assert.fail("No numberOfSets stat")
}
val numberOfGames = results.computedStat(Stat.NUMBER_OF_GAMES)
if (numberOfGames != null) {
assertEquals(3, numberOfGames.value.toInt())
} else {
Assert.fail("No numberOfSets stat")
} }
}
} }

@ -126,15 +126,15 @@ class Calculator {
gTotalHands += sessionSet.estimatedHands gTotalHands += sessionSet.estimatedHands
gBBSum += sessionSet.bbNetResult gBBSum += sessionSet.bbNetResult
hourlyRate = gSum / duration * 3600.0 hourlyRate = gSum / duration
hourlyRateBB = gBBSum / duration * 3600.0 hourlyRateBB = gBBSum / duration
if (options.evolutionValues == Options.EvolutionValues.DATED) { if (options.evolutionValues == Options.EvolutionValues.DATED) {
results.addEvolutionValue(gSum, duration, NETRESULT) results.addEvolutionValue(gSum, duration, NETRESULT)
results.addEvolutionValue(gSum / duration * 3600.0, duration, HOURLY_RATE) results.addEvolutionValue(gSum / duration, duration, HOURLY_RATE)
results.addEvolutionValue(Stat.netBBPer100Hands(gBBSum, gTotalHands), duration, NET_BB_PER_100_HANDS) results.addEvolutionValue(Stat.netBBPer100Hands(gBBSum, gTotalHands), duration, NET_BB_PER_100_HANDS)
results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE) results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE)
results.addEvolutionValue(gIndex.toDouble(), duration, NUMBER_OF_GROUPS) results.addEvolutionValue(gIndex.toDouble(), duration, NUMBER_OF_SETS)
results.addEvolutionValue(sessionSet.duration.toDouble(), duration, DURATION) results.addEvolutionValue(sessionSet.duration.toDouble(), duration, DURATION)
results.addEvolutionValue(duration / gIndex, duration, AVERAGE_DURATION) results.addEvolutionValue(duration / gIndex, duration, AVERAGE_DURATION)
results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB) results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB)
@ -149,11 +149,11 @@ class Calculator {
ComputedStat(HOURLY_RATE, hourlyRate), ComputedStat(HOURLY_RATE, hourlyRate),
ComputedStat(AVERAGE, average), ComputedStat(AVERAGE, average),
ComputedStat(DURATION, duration), ComputedStat(DURATION, duration),
ComputedStat(NUMBER_OF_GROUPS, sessionSets.size.toDouble()), ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()),
ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()), ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()),
ComputedStat(AVERAGE_DURATION, (duration / 3600.0) / sessions.size), ComputedStat(AVERAGE_DURATION, duration / sessions.size),
ComputedStat(NET_BB_PER_100_HANDS, Stat.netBBPer100Hands(bbSum, totalHands)), ComputedStat(NET_BB_PER_100_HANDS, Stat.netBBPer100Hands(bbSum, totalHands)),
ComputedStat(HOURLY_RATE_BB, bbSum / duration * 3600.0), ComputedStat(HOURLY_RATE_BB, bbSum / duration),
ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount), ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount),
ComputedStat(WIN_RATIO, (winningSessionCount / sessions.size).toDouble()), ComputedStat(WIN_RATIO, (winningSessionCount / sessions.size).toDouble()),
ComputedStat(AVERAGE_BUYIN, totalBuyin / sessions.size), ComputedStat(AVERAGE_BUYIN, totalBuyin / sessions.size),
@ -180,7 +180,7 @@ class Calculator {
results.addStats(setOf( results.addStats(setOf(
ComputedStat(STANDARD_DEVIATION, standardDeviation), ComputedStat(STANDARD_DEVIATION, standardDeviation),
ComputedStat(Stat.STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation) ComputedStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation)
)) ))
} }

@ -12,7 +12,7 @@ enum class Stat : RowRepresentable {
NETRESULT, NETRESULT,
HOURLY_RATE, HOURLY_RATE,
AVERAGE, AVERAGE,
NUMBER_OF_GROUPS, NUMBER_OF_SETS,
NUMBER_OF_GAMES, NUMBER_OF_GAMES,
DURATION, DURATION,
AVERAGE_DURATION, AVERAGE_DURATION,
@ -58,7 +58,7 @@ enum class Stat : RowRepresentable {
NETRESULT -> R.string.net_result NETRESULT -> R.string.net_result
HOURLY_RATE -> R.string.average_hour_rate HOURLY_RATE -> R.string.average_hour_rate
AVERAGE -> R.string.average AVERAGE -> R.string.average
NUMBER_OF_GROUPS -> R.string.number_of_groups NUMBER_OF_SETS -> R.string.number_of_groups
NUMBER_OF_GAMES -> R.string.number_of_games NUMBER_OF_GAMES -> R.string.number_of_games
DURATION -> R.string.duration DURATION -> R.string.duration
AVERAGE_DURATION -> R.string.average_duration AVERAGE_DURATION -> R.string.average_duration

@ -106,7 +106,7 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
} }
@Ignore @Ignore
override var estimatedHands: Double = 0.0 override var estimatedHands: Double = 25.0 * (this.timeFrame?.hourlyDuration?.toDouble() ?: 0.0)
@Ignore @Ignore
override var bbNetResult: Double = 0.0 override var bbNetResult: Double = 0.0

@ -1,18 +1,25 @@
package net.pokeranalytics.android.model.realm package net.pokeranalytics.android.model.realm
import io.realm.Realm import io.realm.Realm
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.Ignore
import io.realm.annotations.LinkingObjects
open class SessionSet() : RealmObject() { open class SessionSet() : RealmObject() {
// The timeframe of the set, i.e. its start & end date /**
* The timeframe of the set, i.e. its start & end date
*/
var timeFrame: TimeFrame? = null var timeFrame: TimeFrame? = null
// The list of Session played within the set, i.e. played within the same time frame /**
var sessions: RealmList<Session> = RealmList() * The list of sessions associated with this set
*/
@LinkingObjects("sessionSet")
val sessions: RealmResults<Session>? = null
@Ignore // a duration shortcut @Ignore // a duration shortcut
var duration: Long = 0L var duration: Long = 0L
@ -28,12 +35,15 @@ open class SessionSet() : RealmObject() {
@Ignore // a netResult shortcut @Ignore // a netResult shortcut
var netResult: Double = 0.0 var netResult: Double = 0.0
get () {
return this.sessions?.sumByDouble { it.value } ?: 0.0
}
@Ignore // a duration shortcut @Ignore // a duration shortcut
var hourlyRate: Double = 0.0 var hourlyRate: Double = 0.0
@Ignore @Ignore
var estimatedHands: Double = 0.0 var estimatedHands: Double = 25.0 * (this.timeFrame?.hourlyDuration?.toDouble() ?: 0.0)
@Ignore @Ignore
var bbNetResult: Double = 0.0 var bbNetResult: Double = 0.0

@ -7,6 +7,7 @@ import io.realm.RealmResults
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects import io.realm.annotations.LinkingObjects
import net.pokeranalytics.android.exceptions.ModelException import net.pokeranalytics.android.exceptions.ModelException
import timber.log.Timber
import java.util.* import java.util.*
open class TimeFrame : RealmObject() { open class TimeFrame : RealmObject() {
@ -82,19 +83,22 @@ open class TimeFrame : RealmObject() {
var query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java) var query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java)
query.isNotNull("timeFrame") query.isNotNull("timeFrame")
Timber.d("this> sd = : ${this.startDate}, ed = ${this.endDate}")
if (this.endDate == null) { if (this.endDate == null) {
query.greaterThan("timeFrame.startDate", this.startDate.time).or().greaterThan("timeFrame.endDate", this.startDate.time) query.greaterThan("timeFrame.startDate", this.startDate.time).or().greaterThan("timeFrame.endDate", this.startDate.time)
} else { } else {
val endDate = this.endDate!! val endDate = this.endDate!!
query query
.greaterThan("timeFrame.startDate", this.startDate)
.lessThan("timeFrame.endDate", this.startDate)
.or()
.greaterThan("timeFrame.startDate", endDate)
.lessThan("timeFrame.endDate", endDate)
.or()
.lessThan("timeFrame.startDate", this.startDate) .lessThan("timeFrame.startDate", this.startDate)
.greaterThan("timeFrame.endDate", this.startDate)
.or()
.lessThan("timeFrame.startDate", endDate)
.greaterThan("timeFrame.endDate", endDate) .greaterThan("timeFrame.endDate", endDate)
.or()
.greaterThan("timeFrame.startDate", this.startDate)
.lessThan("timeFrame.endDate", endDate)
} }
val sessionGroups = query.findAll() val sessionGroups = query.findAll()
@ -138,6 +142,9 @@ open class TimeFrame : RealmObject() {
} ?: run { } ?: run {
throw ModelException("Session should never be null here") throw ModelException("Session should never be null here")
} }
Timber.d("sd = : ${set.timeFrame?.startDate}, ed = ${set.timeFrame?.endDate}")
// this.session?.sessionSet = set // this.session?.sessionSet = set
// set.sessions.add(this.session) // set.sessions.add(this.session)
// realm.commitTransaction() // realm.commitTransaction()
@ -163,10 +170,10 @@ open class TimeFrame : RealmObject() {
// Realm Update // Realm Update
// val realm = Realm.getDefaultInstance() // val realm = Realm.getDefaultInstance()
realm.beginTransaction() // realm.beginTransaction()
if (!sessionSet.sessions.contains(this.session)) {
sessionSet.sessions.add(this.session) this.session?.sessionSet = sessionSet
}
// realm.copyToRealmOrUpdate(groupTimeFrame) // realm.copyToRealmOrUpdate(groupTimeFrame)
// realm.commitTransaction() // realm.commitTransaction()
@ -181,6 +188,7 @@ open class TimeFrame : RealmObject() {
var startDate: Date = this.startDate var startDate: Date = this.startDate
var endDate: Date? = this.endDate var endDate: Date? = this.endDate
// find earlier and later dates from all sets
val timeFrames = sessionSets.mapNotNull { it.timeFrame } val timeFrames = sessionSets.mapNotNull { it.timeFrame }
timeFrames.forEach { tf -> timeFrames.forEach { tf ->
if (tf.startDate.before(startDate)) { if (tf.startDate.before(startDate)) {
@ -200,11 +208,8 @@ open class TimeFrame : RealmObject() {
} }
// get all sessions from sets // get all sessions from sets
var sessions = sessionSets.flatMap { it.sessions } var sessions = mutableSetOf<Session>()
sessionSets.forEach { it.sessions?.asIterable()?.let { it1 -> sessions.addAll(it1) } }
// Start Realm updates
// val realm = Realm.getDefaultInstance()
// realm.beginTransaction()
// delete all sets // delete all sets
sessionSets.deleteAllFromRealm() sessionSets.deleteAllFromRealm()
@ -220,15 +225,14 @@ open class TimeFrame : RealmObject() {
// Add the session linked to this timeframe to the new sessionGroup // Add the session linked to this timeframe to the new sessionGroup
this.sessions?.first()?.let { this.sessions?.first()?.let {
set.sessions.add(it) it.sessionSet = set
} ?: run { } ?: run {
throw ModelException("TimeFrame should never be null here") throw ModelException("TimeFrame should never be null here")
} }
// Add all orphan sessions // Add all orphan sessions
set.sessions.addAll(sessions) sessions.forEach { it.sessionSet = set }
// realm.commitTransaction()
} }
} }

Loading…
Cancel
Save