From 9c3134d86675c009741ec0920322b81a9003b355 Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 8 Mar 2019 12:46:27 +0100 Subject: [PATCH] Session/SessionSet date refactoring, TimeFrame removed --- .../android/ExampleInstrumentedUnitTest.kt | 518 ---------------- .../android/StatsInstrumentedUnitTest.kt | 573 ++++++++++++++++++ .../android/PokerAnalyticsApplication.kt | 15 +- .../android/calculus/Calculator.kt | 19 +- .../android/calculus/Computable.kt | 1 - .../model/extensions/SessionExtensions.kt | 45 +- .../android/model/interfaces/Timed.kt | 30 + .../android/model/realm/Session.kt | 199 +++--- .../android/model/realm/SessionSet.kt | 63 +- .../android/model/realm/TimeFrame.kt | 568 ++++++++--------- .../model/utils/FavoriteSessionFinder.kt | 2 +- .../android/model/utils/SessionSetManager.kt | 140 +++++ .../android/ui/fragment/SessionFragment.kt | 4 +- .../android/ui/fragment/StatsFragment.kt | 3 +- .../components/SessionObserverFragment.kt | 2 +- .../android/ui/view/SessionRowView.kt | 14 +- app/src/main/res/values/strings.xml | 2 +- 17 files changed, 1253 insertions(+), 945 deletions(-) delete mode 100644 app/src/androidTest/java/net/pokeranalytics/android/ExampleInstrumentedUnitTest.kt create mode 100644 app/src/androidTest/java/net/pokeranalytics/android/StatsInstrumentedUnitTest.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt diff --git a/app/src/androidTest/java/net/pokeranalytics/android/ExampleInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/ExampleInstrumentedUnitTest.kt deleted file mode 100644 index b9ff447a..00000000 --- a/app/src/androidTest/java/net/pokeranalytics/android/ExampleInstrumentedUnitTest.kt +++ /dev/null @@ -1,518 +0,0 @@ -package net.pokeranalytics.android - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.realm.RealmResults -import net.pokeranalytics.android.calculus.Calculator -import net.pokeranalytics.android.calculus.ComputedResults -import net.pokeranalytics.android.calculus.SessionGroup -import net.pokeranalytics.android.calculus.Stat -import net.pokeranalytics.android.model.realm.Session -import net.pokeranalytics.android.model.realm.SessionSet -import net.pokeranalytics.android.model.realm.TimeFrame -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith -import java.text.SimpleDateFormat -import java.util.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) - -class ExampleInstrumentedUnitTest : RealmInstrumentedUnitTest() { - - // convenience extension - fun Session.Companion.testInstance(netResult: Double, startDate: Date, endDate: Date?): Session { - val session: Session = Session.newInstance(super.mockRealm, false) - session.result?.netResult = netResult - session.timeFrame?.setDate(startDate, endDate) - return session - } - - @Test - fun testSessionStats() { - - val realm = this.mockRealm - realm.beginTransaction() - - val s1 = realm.createObject(Session::class.java, "1") - val 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) - - s1.result?.buyin = 100.0 // net result = -100 - s2.result?.buyin = 200.0 - s2.result?.cashout = 500.0 // net result = 300 - - s1.cgBigBlind = 0.5 // bb net result = -200bb - s2.cgBigBlind = 2.0 // bb net result = 150bb - - realm.insert(s1) - realm.insert(s2) - 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() - - s1.timeFrame?.setDate(sd1, ed1) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 - s2.timeFrame?.setDate(sd2, ed2) // netDuration = 3h, 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) - - val 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 sum = results.computedStat(Stat.NETRESULT) - if (sum != null) { - assertEquals(200.0, sum.value, delta) - } else { - Assert.fail("No Net result stat") - } - - val average = results.computedStat(Stat.AVERAGE) - if (average != null) { - assertEquals(100.0, average.value, delta) - } else { - Assert.fail("No AVERAGE stat") - } - - val duration = results.computedStat(Stat.DURATION) - if (duration != null) { - assertEquals(4.0, duration.value, delta) - } else { - Assert.fail("No netDuration 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 std = results.computedStat(Stat.STANDARD_DEVIATION) - if (std != null) { - assertEquals(200.0, std.value, delta) - } else { - Assert.fail("No std 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 std100 = results.computedStat(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS) - if (std100 != null) { - assertEquals(559.01, std100.value, delta) - } else { - Assert.fail("No std100 stat") - } - - } - - @Test - fun testOverlappingSessions1() { - - val realm = this.mockRealm - realm.beginTransaction() - - val s1 = realm.createObject(Session::class.java, "1") - val 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) - - 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") - - - s1.timeFrame?.setDate(sd1, ed1) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 - s2.timeFrame?.setDate(sd2, ed2) // netDuration = 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) - - val 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() - - val s1 = realm.createObject(Session::class.java, "1") - val s2 = realm.createObject(Session::class.java, "2") - val 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) - - 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") - - s1.timeFrame?.setDate(sd1, ed1) // netDuration = 4h - s2.timeFrame?.setDate(sd2, ed2) // netDuration = 4h - s3.timeFrame?.setDate(sd3, ed3) // netDuration = 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) - - val 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") - } - - } - - var sessions: RealmResults? = null - -// @Test - fun testOverlappingSessionDeletion() { - - val realm = this.mockRealm - this.sessions = realm.where(Session::class.java).findAll() // monitor session deletions - - this.sessions?.addChangeListener { t, changeSet -> - - val deletedSessions = realm.where(Session::class.java).`in`("id", changeSet.deletions.toTypedArray()).findAll() - deletedSessions.forEach { it.cleanup() } - - } - - realm.beginTransaction() - - val s1 = realm.createObject(Session::class.java, "1") - val s2 = realm.createObject(Session::class.java, "2") - val 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) // netDuration = 4h - s2.timeFrame?.setDate(sd2, ed2) // netDuration = 4h - s3.timeFrame?.setDate(sd3, ed3) // netDuration = 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) - - val 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 netDuration stat") - } - - realm.executeTransaction { - s1.deleteFromRealm() - } - - val group2 = SessionGroup(name = "test", sessions = sessions) - val results2: ComputedResults = Calculator.compute(group2, options) - - val duration2 = results2.computedStat(Stat.DURATION) - if (duration2 != null) { - assertEquals(7.0, duration2.value, delta) - } else { - Assert.fail("No duration2 stat") - } - - } - - - @Test - fun testSessionSetCount() { - - val realm = this.mockRealm - - realm.beginTransaction() - val s1 = realm.createObject(Session::class.java, "1") - - s1.timeFrame = realm.createObject(TimeFrame::class.java) - - s1.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java) - - realm.insert(s1) - - 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") - - s1.timeFrame?.setDate(sd1, ed1) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 - - realm.copyToRealmOrUpdate(s1) - realm.commitTransaction() - - val sets = realm.where(SessionSet::class.java).findAll() - - Assert.assertEquals(1, sets.size) - - val set = sets.first() - if (set != null) { - Assert.assertEquals(sd1.time, set.timeFrame?.startDate?.time) - Assert.assertEquals(ed1.time, set.timeFrame?.endDate?.time) - } else { - Assert.fail("No set") - } - - } - - @Test - fun testSessionSetCount2() { - - val realm = this.mockRealm - - realm.beginTransaction() - val s1 = realm.createObject(Session::class.java, "1") - val 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) - - 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/2/2018 09:00") - val ed2 = sdf.parse("01/2/2018 10:00") - - s1.timeFrame?.let { - it.setStart(sd1) - it.setEnd(ed1) - } - - s2.timeFrame?.let { - it.setStart(sd2) - it.setEnd(ed2) - } - -// s1.timeFrame?.setDate(sd1, ed1) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 -// s2.timeFrame?.setDate(sd2, ed2) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 - - realm.copyToRealmOrUpdate(s1) - realm.copyToRealmOrUpdate(s2) - - realm.commitTransaction() - - val sets = realm.where(SessionSet::class.java).findAll() - - Assert.assertEquals(2, sets.size) - -// val set = sets.first() -// if (set != null) { -// Assert.assertEquals(sd1.time, set.timeFrame?.startDate?.time) -// Assert.assertEquals(ed1.time, set.timeFrame?.endDate?.time) -// } else { -// Assert.fail("No set") -// } - - } -// -// @Test -// fun testDurationConversion() { -// -// val duration = 6.7555561274509826 -// val longDuration = duration.toLong() -// val formatted = longDuration.toMinutes() -// -// assert(formatted == "11:00") -// } - - -} \ No newline at end of file diff --git a/app/src/androidTest/java/net/pokeranalytics/android/StatsInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/StatsInstrumentedUnitTest.kt new file mode 100644 index 00000000..1a5c7e84 --- /dev/null +++ b/app/src/androidTest/java/net/pokeranalytics/android/StatsInstrumentedUnitTest.kt @@ -0,0 +1,573 @@ +package net.pokeranalytics.android + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.realm.RealmResults +import net.pokeranalytics.android.calculus.Calculator +import net.pokeranalytics.android.calculus.ComputedResults +import net.pokeranalytics.android.calculus.SessionGroup +import net.pokeranalytics.android.calculus.Stat +import net.pokeranalytics.android.model.realm.Session +import net.pokeranalytics.android.model.realm.SessionSet +import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import java.text.SimpleDateFormat +import java.util.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class StatsInstrumentedUnitTest : RealmInstrumentedUnitTest() { + + // convenience extension + fun Session.Companion.testInstance(netResult: Double, startDate: Date, endDate: Date?): Session { + val session: Session = Session.newInstance(super.mockRealm, false) + session.result?.netResult = netResult + session.startDate = startDate + session.endDate = endDate + return session + } + + @Test + fun testSessionStats() { + + val realm = this.mockRealm + realm.beginTransaction() + + val s1 = realm.createObject(Session::class.java, "1") + val s2 = realm.createObject(Session::class.java, "2") + + s1.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java) + s2.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java) + + s1.result?.buyin = 100.0 // net result = -100 + s2.result?.buyin = 200.0 + s2.result?.cashout = 500.0 // net result = 300 + + s1.cgBigBlind = 0.5 // bb net result = -200bb + s2.cgBigBlind = 2.0 // bb net result = 150bb + + realm.insert(s1) + realm.insert(s2) + 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() + + s1.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + + realm.copyToRealmOrUpdate(s1) + realm.copyToRealmOrUpdate(s2) + + realm.commitTransaction() + + val sessions = realm.where(Session::class.java).findAll() + val group = SessionGroup(name = "test", sessions = sessions) + + val 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 sum = results.computedStat(Stat.NETRESULT) + if (sum != null) { + assertEquals(200.0, sum.value, delta) + } else { + Assert.fail("No Net result stat") + } + + val average = results.computedStat(Stat.AVERAGE) + if (average != null) { + assertEquals(100.0, average.value, delta) + } else { + Assert.fail("No AVERAGE stat") + } + + val duration = results.computedStat(Stat.DURATION) + if (duration != null) { + assertEquals(4.0, duration.value, delta) + } else { + Assert.fail("No netDuration 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 std = results.computedStat(Stat.STANDARD_DEVIATION) + if (std != null) { + assertEquals(200.0, std.value, delta) + } else { + Assert.fail("No std 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 std100 = results.computedStat(Stat.STANDARD_DEVIATION_BB_PER_100_HANDS) + if (std100 != null) { + assertEquals(559.01, std100.value, delta) + } else { + Assert.fail("No std100 stat") + } + + } + + @Test + fun testOverlappingSessions1() { + + val realm = this.mockRealm + realm.beginTransaction() + + val s1 = realm.createObject(Session::class.java, "1") + val s2 = realm.createObject(Session::class.java, "2") + + 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) + + 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") + + s1.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + + // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 + // netDuration = 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) + + val 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() + + val s1 = realm.createObject(Session::class.java, "1") + val s2 = realm.createObject(Session::class.java, "2") + val s3 = realm.createObject(Session::class.java, "3") + + realm.insert(s1) + realm.insert(s2) + realm.insert(s3) + + 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") // 4h + val sd2 = sdf.parse("01/1/2019 07:00") + val ed2 = sdf.parse("01/1/2019 11:00") // 4h + val sd3 = sdf.parse("01/1/2019 03:00") + val ed3 = sdf.parse("01/1/2019 06:00") // 3h + + s1.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + s3.startDate = sd3 + s3.endDate = ed3 + + 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) + + val 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") + } + + } + + private var sessions: RealmResults? = null + + // @Test + fun testOverlappingSessionDeletion() { + + val realm = this.mockRealm + this.sessions = realm.where(Session::class.java).findAll() // monitor session deletions + + this.sessions?.addChangeListener { _, changeSet -> + + val deletedSessions = + realm.where(Session::class.java).`in`("id", changeSet.deletions.toTypedArray()).findAll() + deletedSessions.forEach { it.cleanup() } + + } + + realm.beginTransaction() + + val s1 = realm.createObject(Session::class.java, "1") + val s2 = realm.createObject(Session::class.java, "2") + val s3 = realm.createObject(Session::class.java, "3") + + 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.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + s3.startDate = sd3 + s3.endDate = ed3 + + 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) + + val 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 netDuration stat") + } + + realm.executeTransaction { + s1.deleteFromRealm() + } + + val group2 = SessionGroup(name = "test", sessions = sessions) + val results2: ComputedResults = Calculator.compute(group2, options) + + val duration2 = results2.computedStat(Stat.DURATION) + if (duration2 != null) { + assertEquals(7.0, duration2.value, delta) + } else { + Assert.fail("No duration2 stat") + } + + } + + + @Test + fun testSessionSetCount() { + + val realm = this.mockRealm + + realm.beginTransaction() + val s1 = realm.createObject(Session::class.java, "1") + + s1.result = realm.createObject(net.pokeranalytics.android.model.realm.Result::class.java) + + realm.insert(s1) + + 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") + + s1.startDate = sd1 // timeFrame?.setDate(sd1, ed1) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 + s1.endDate = ed1 + + realm.copyToRealmOrUpdate(s1) + realm.commitTransaction() + + val sets = realm.where(SessionSet::class.java).findAll() + + Assert.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) + } else { + Assert.fail("No set") + } + + } + + @Test + fun testSessionSetCount2() { + + val realm = this.mockRealm + + realm.beginTransaction() + val s1 = realm.createObject(Session::class.java, "1") + val s2 = realm.createObject(Session::class.java, "2") + + 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) + + 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/2/2018 09:00") + val ed2 = sdf.parse("01/2/2018 10:00") + + s1.startDate = sd1 + s1.endDate = ed1 + + s2.startDate = sd2 + s2.endDate = ed2 + +// s1.timeFrame?.setDate(sd1, ed1) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 +// s2.timeFrame?.setDate(sd2, ed2) // netDuration = 1h, hourly = -100, bb100 = -200bb / 25hands * 100 = -800 + + realm.copyToRealmOrUpdate(s1) + realm.copyToRealmOrUpdate(s2) + + realm.commitTransaction() + + val sets = realm.where(SessionSet::class.java).findAll() + + Assert.assertEquals(2, sets.size) + +// val set = sets.first() +// if (set != null) { +// Assert.assertEquals(sd1.time, set.timeFrame?.startDate?.time) +// Assert.assertEquals(ed1.time, set.timeFrame?.endDate?.time) +// } else { +// Assert.fail("No set") +// } + + } +// +// @Test +// fun testDurationConversion() { +// +// val duration = 6.7555561274509826 +// val longDuration = duration.toLong() +// val formatted = longDuration.toMinutes() +// +// assert(formatted == "11:00") +// } + + + @Test + fun testSessionRestartInOverlappingSessions() { + + val realm = this.mockRealm + realm.beginTransaction() + + val s1 = realm.createObject(Session::class.java, "1") + val s2 = realm.createObject(Session::class.java, "2") + val s3 = realm.createObject(Session::class.java, "3") + + realm.insert(s1) + realm.insert(s2) + realm.insert(s3) + + 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") // 4h + val sd2 = sdf.parse("01/1/2019 07:00") + val ed2 = sdf.parse("01/1/2019 11:00") // 4h + val sd3 = sdf.parse("01/1/2019 03:00") + val ed3 = sdf.parse("01/1/2019 06:00") // 3h + + s1.startDate = sd1 + s1.endDate = ed1 + s2.startDate = sd2 + s2.endDate = ed2 + s3.startDate = sd3 + s3.endDate = ed3 + + realm.copyToRealmOrUpdate(s1) + realm.copyToRealmOrUpdate(s2) + realm.copyToRealmOrUpdate(s3) + + realm.commitTransaction() + + realm.executeTransaction { + s1.endDate = null + } + + val sessions = realm.where(Session::class.java).findAll() + val group = SessionGroup(name = "test", sessions = sessions) + + val 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(7.0, duration.value, delta) + } else { + Assert.fail("No Net result stat") + } + + val numberOfSets = results.computedStat(Stat.NUMBER_OF_SETS) + if (numberOfSets != null) { + assertEquals(2, numberOfSets.value.toInt()) + } else { + Assert.fail("No numberOfSets stat") + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt index 9a35fb1d..96e87c94 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -63,7 +63,7 @@ class PokerAnalyticsApplication : Application() { this.createDefaultData() if (BuildConfig.DEBUG) { - this.createFakeSessions() // debug +// this.createFakeSessions() // debug } } @@ -158,11 +158,14 @@ class PokerAnalyticsApplication : Application() { // val timeFrame = TimeFrame() - session.timeFrame?.let { - // it.startDate = startDate -// it.endDate = endDate - it.setDate(startDate, endDate) - } + session.startDate = startDate + session.endDate = endDate + +// session.timeFrame?.let { +// // it.startDate = startDate +//// it.endDate = endDate +// it.setDate(startDate, endDate) +// } // session.timeFrame = timeFrame session.creationDate = startDate 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 f7ed7486..862e8973 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt @@ -64,7 +64,7 @@ class Calculator { */ fun computeGroups(groups: List, options: Options): List { - var computedResults: MutableList = mutableListOf() + val computedResults = mutableListOf() groups.forEach { group -> // Computes actual sessionGroup stats val results: ComputedResults = Calculator.compute(group, options = options) @@ -90,9 +90,9 @@ class Calculator { fun compute(sessionGroup: SessionGroup, options: Options) : ComputedResults { val sessions: List = sessionGroup.sessions - var sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet() + val sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet() - var results: ComputedResults = ComputedResults(sessionGroup) + val results: ComputedResults = ComputedResults(sessionGroup) var sum: Double = 0.0 var totalHands: Double = 0.0 @@ -159,7 +159,7 @@ class Calculator { results.addEvolutionValue(gSum / duration, duration, HOURLY_RATE) results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE) results.addEvolutionValue(gIndex.toDouble(), duration, NUMBER_OF_SETS) - results.addEvolutionValue(sessionSet.duration.toDouble(), duration, DURATION) + results.addEvolutionValue(sessionSet.netDuration.toDouble(), duration, DURATION) results.addEvolutionValue(duration / gIndex, duration, AVERAGE_DURATION) results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB) } @@ -175,19 +175,24 @@ class Calculator { var average = 0.0 if (sessions.size > 0) { average = sum / sessions.size.toDouble() - val avgDuration = duration / sessions.size val winRatio = (winningSessionCount / sessions.size).toDouble() val avgBuyin = totalBuyin / sessions.size results.addStats(setOf( ComputedStat(AVERAGE, average), - ComputedStat(AVERAGE_DURATION, avgDuration), ComputedStat(WIN_RATIO, winRatio), ComputedStat(AVERAGE_BUYIN, avgBuyin) )) } - // Create stats + if (sessionSets.size > 0) { + val avgDuration = duration / sessionSets.size + results.addStats(setOf( + ComputedStat(AVERAGE_DURATION, avgDuration) + )) + } + + // Create stats results.addStats(setOf( ComputedStat(NETRESULT, sum), ComputedStat(HOURLY_RATE, hourlyRate), diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Computable.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Computable.kt index fd4fa66f..8af082fe 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Computable.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Computable.kt @@ -19,7 +19,6 @@ interface SessionInterface : Summable { var bigBlindSessionCount: Int // 0 or 1 var buyin: Double var bbPer100Hands: Double - } /** 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 cc59bdca..ee31132d 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 @@ -18,24 +18,35 @@ enum class SessionState { */ fun Session.getState(): SessionState { - if (timeFrame == null) { - return SessionState.PENDING - } +// if (timeFrame == null) { +// return SessionState.PENDING +// } - val endDate = timeFrame?.endDate - timeFrame?.let {sessionTimeFrame -> - timeFrame?.startDate?.let {startDate -> - if (startDate > Date()) { - return SessionState.PLANNED - } else if (endDate != null) { - return SessionState.FINISHED - } else if (sessionTimeFrame.paused) { - return SessionState.PAUSED - } else { - return SessionState.STARTED - } - } + if (startDate > Date()) { + return SessionState.PLANNED + } else if (endDate != null) { + return SessionState.FINISHED + } else if (this.pauseDate != null) { + return SessionState.PAUSED + } else { + return SessionState.STARTED } - return SessionState.INVALID + +// val endDate = timeFrame?.endDate +// timeFrame?.let {sessionTimeFrame -> +// timeFrame?.startDate?.let {startDate -> +// if (startDate > Date()) { +// return SessionState.PLANNED +// } else if (endDate != null) { +// return SessionState.FINISHED +// } else if (sessionTimeFrame.paused) { +// return SessionState.PAUSED +// } else { +// return SessionState.STARTED +// } +// } +// } + +// return SessionState.INVALID } 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 new file mode 100644 index 00000000..2db4f0bb --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt @@ -0,0 +1,30 @@ +package net.pokeranalytics.android.model.interfaces + +import java.util.* + +interface Timed { + + var startDate: Date + + fun endDate() : Date + + var breakDuration: Long + + var netDuration: Long + + /** + * Computes the net netDuration of the session + */ + fun computeDuration() { + this.netDuration = this.endDate().time - this.startDate.time - this.breakDuration + } + +// fun hourlyDuration() : Double { +// return this.netDuration / 3600000.0 // 3.6 millions of milliseconds +// } + + var hourlyDuration: Double + get() = this.netDuration / 3600000.0 + set(value) = TODO() + +} \ No newline at end of file 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 7f59f289..9d598181 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 @@ -19,6 +19,8 @@ import net.pokeranalytics.android.model.TableSize import net.pokeranalytics.android.model.extensions.SessionState import net.pokeranalytics.android.model.extensions.getState import net.pokeranalytics.android.model.interfaces.Savable +import net.pokeranalytics.android.model.interfaces.Timed +import net.pokeranalytics.android.model.utils.SessionSetManager import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException import net.pokeranalytics.android.ui.view.RowRepresentable @@ -31,8 +33,8 @@ import net.pokeranalytics.android.util.* import java.util.* import kotlin.collections.ArrayList -open class Session : RealmObject(), SessionInterface, Savable, - StaticRowRepresentableDataSource, RowRepresentable { +open class Session : RealmObject(), SessionInterface, Savable, StaticRowRepresentableDataSource, RowRepresentable, + Timed { enum class Type { CASH_GAME, @@ -60,13 +62,54 @@ open class Session : RealmObject(), SessionInterface, Savable, // The result of the main user var result: Result? = null - // The time frame of the Session, i.e. the start & end date - var timeFrame: TimeFrame? = null + // Timed interface + + override var startDate: Date = Date() + set(value) { + field = value + this.computeDuration() + if (this.endDate != null && this.startDate.after(this.endDate)) { + this.endDate = null + } + this.dateChanged() + } + + var endDate: Date? = null set(value) { field = value - value?.let { it.notifySessionDateChange(this) } + this.computeDuration() + this.dateChanged() } + private fun dateChanged() { + if (this.endDate != null) { + SessionSetManager.updateTimeline(this) + } else if (this.sessionSet != null) { + SessionSetManager.removeFromTimeline(this) + } + } + + override fun endDate(): Date { + return this.endDate ?: Date() + } + + override var breakDuration: Long = 0L + set(value) { + field = value + this.computeDuration() + } + + override var netDuration: Long = 0L + + var pauseDate: Date? = null + + // The time frame of the Session, i.e. the start & end date +// var timeFrame: TimeFrame? = null +// set(value) { +// field = value +// value?.let { it.notifySessionDateChange(this) } +// } + // The time frame sessionGroup, which can contain multiple sessions override var sessionSet: SessionSet? = null @@ -129,16 +172,19 @@ open class Session : RealmObject(), SessionInterface, Savable, * Return if this session is a tournament */ fun isTournament(): Boolean { - return type == Type.TOURNAMENT.ordinal + return this.type == Type.TOURNAMENT.ordinal } /** * Return if this session is a cash game */ fun isCashGame(): Boolean { - return type == Type.CASH_GAME.ordinal + return this.type == Type.CASH_GAME.ordinal } + + // Stats + @Ignore // SessionInterface value override var value: Double = 0.0 get() { @@ -146,7 +192,7 @@ open class Session : RealmObject(), SessionInterface, Savable, } @Ignore - override var estimatedHands: Double = 25.0 * (this.timeFrame?.hourlyDuration ?: 0.0) + override var estimatedHands: Double = 25.0 * this.hourlyDuration @Ignore override var bbNetResult: Double = 0.0 @@ -171,25 +217,15 @@ open class Session : RealmObject(), SessionInterface, Savable, @Ignore override var buyin: Double = 0.0 get() { - this.result?.let { - it.buyin?.let { + this.result?.let { result -> + result.buyin?.let { return it } } return 0.0 } - @Ignore - var netDuration: Long = 0L - get() { - this.timeFrame?.let { - return it.netDuration - } - return 0L - } - - @Ignore - var hourlyRate: Double = 0.0 + val hourlyRate: Double get() { this.result?.let { result -> return result.net / this.netDuration.toDouble() @@ -206,16 +242,19 @@ open class Session : RealmObject(), SessionInterface, Savable, realm.executeTransaction { when (getState()) { SessionState.PENDING, SessionState.PLANNED -> { - val sessionTimeFrame = this.timeFrame ?: realm.createObject(TimeFrame::class.java) - sessionTimeFrame.setStart(Date()) + this.startDate = Date() + +// val sessionTimeFrame = this.timeFrame ?: realm.createObject(TimeFrame::class.java) +// sessionTimeFrame.setStart(Date()) // sessionTimeFrame.setDate(Date(), null) - this.timeFrame = sessionTimeFrame +// this.timeFrame = sessionTimeFrame } SessionState.PAUSED -> { - this.timeFrame?.paused = false - this.timeFrame?.pauseDate = null +// this.timeFrame?.paused = false + this.pauseDate = null } else -> { + throw IllegalStateException("unmanaged session state") } } } @@ -228,9 +267,10 @@ open class Session : RealmObject(), SessionInterface, Savable, realm.executeTransaction { when (getState()) { SessionState.STARTED -> { - this.timeFrame?.paused = true - this.timeFrame?.pauseDate = Date() +// this.?.paused = true + this.pauseDate = Date() } + else -> throw IllegalStateException("unmanaged state") } } } @@ -242,9 +282,10 @@ open class Session : RealmObject(), SessionInterface, Savable, realm.executeTransaction { when (getState()) { SessionState.STARTED, SessionState.PAUSED -> { - this.timeFrame?.paused = false - this.timeFrame?.pauseDate = null - this.timeFrame?.setEnd(Date()) + this.end() +// this.timeFrame?.paused = false +// this.pauseDate = null +// this.endDate = Date() // this.timeFrame?.setDate(null, Date()) } else -> throw Exception("Stopping session in unmanaged state") @@ -257,10 +298,17 @@ open class Session : RealmObject(), SessionInterface, Savable, */ fun restart() { realm.executeTransaction { - this.timeFrame?.paused = false - this.timeFrame?.pauseDate = null - this.timeFrame?.setDate(Date(), null) - this.timeFrame?.breakDuration = 0L +// this.timeFrame?.paused = false + this.pauseDate = null + this.startDate = Date() // timeFrame?.setDate(Date(), null) + this.breakDuration = 0L + } + } + + private fun end() { + this.pauseDate = null + if (this.endDate == null) { + this.endDate = Date() } } @@ -270,8 +318,7 @@ open class Session : RealmObject(), SessionInterface, Savable, * Return the netDuration of the current session */ fun getDuration(): String { - val startDate = timeFrame?.startDate ?: Date() - val enDate = timeFrame?.endDate ?: Date() + val enDate = this.endDate ?: Date() return startDate.getDuration(enDate) } @@ -321,13 +368,14 @@ open class Session : RealmObject(), SessionInterface, Savable, // cleanup unnecessary related objects set.deleteFromRealm() - this.timeFrame?.deleteFromRealm() +// this.timeFrame?.deleteFromRealm() this.result?.deleteFromRealm() // make sessions recreate/find their session set sessionsFromSet?.let { sessions -> sessions.forEach { session -> - session.timeFrame?.notifySessionDateChange(session) + // @todo +// session.timeFrame?.notifySessionDateChange(session) } } } @@ -381,7 +429,7 @@ open class Session : RealmObject(), SessionInterface, Savable, HeaderRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT, resId = R.string.hour_rate_without_pauses, - computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate ?: 0.0) + computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate) ) ) @@ -417,19 +465,17 @@ open class Session : RealmObject(), SessionInterface, Savable, return when (row) { SessionRow.BANKROLL -> bankroll?.name ?: NULL_TEXT SessionRow.BLINDS -> getBlinds() - SessionRow.BREAK_TIME -> timeFrame?.breakDuration?.toMinutes() ?: NULL_TEXT + SessionRow.BREAK_TIME -> this.breakDuration.toMinutes() SessionRow.BUY_IN -> buyin.toCurrency() SessionRow.CASHED_OUT, SessionRow.PRIZE, SessionRow.NET_RESULT -> result?.cashout?.toCurrency() ?: NULL_TEXT SessionRow.COMMENT -> if (comment.isNotEmpty()) comment else NULL_TEXT - SessionRow.END_DATE -> if (timeFrame != null) timeFrame?.endDate?.shortDateTime() - ?: NULL_TEXT else NULL_TEXT + SessionRow.END_DATE -> this.endDate?.shortDateTime() ?: NULL_TEXT SessionRow.GAME -> getGameTitle() SessionRow.INITIAL_BUY_IN -> tournamentEntryFee?.toCurrency() ?: NULL_TEXT SessionRow.LOCATION -> location?.name ?: NULL_TEXT SessionRow.PLAYERS -> tournamentNumberOfPlayers?.toString() ?: NULL_TEXT SessionRow.POSITION -> result?.tournamentFinalPosition?.toString() ?: NULL_TEXT - SessionRow.START_DATE -> if (timeFrame != null) timeFrame?.startDate?.shortDateTime() - ?: NULL_TEXT else NULL_TEXT + SessionRow.START_DATE -> this.startDate.shortDateTime() SessionRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT SessionRow.TIPS -> result?.tips?.toCurrency() ?: NULL_TEXT SessionRow.TOURNAMENT_TYPE -> tournamentType?.name ?: NULL_TEXT @@ -481,12 +527,12 @@ open class Session : RealmObject(), SessionInterface, Savable, } SessionRow.BUY_IN -> { // Add first & second buttons values, current value & set the 2 edit texts - if (cgBigBlind != null) { - data.add(RowRepresentableEditDescriptor(100.0 * (cgBigBlind ?: 0.0))) - data.add(RowRepresentableEditDescriptor(200.0 * (cgBigBlind ?: 0.0))) - } else if (tournamentEntryFee != null) { - data.add(RowRepresentableEditDescriptor((tournamentEntryFee ?: 0.0) * 1.0)) - data.add(RowRepresentableEditDescriptor((tournamentEntryFee ?: 0.0) * 2.0)) + if (this.cgBigBlind != null) { + data.add(RowRepresentableEditDescriptor(100.0 * (this.cgBigBlind ?: 0.0))) + data.add(RowRepresentableEditDescriptor(200.0 * (this.cgBigBlind ?: 0.0))) + } else if (this.tournamentEntryFee != null) { + data.add(RowRepresentableEditDescriptor((this.tournamentEntryFee ?: 0.0) * 1.0)) + data.add(RowRepresentableEditDescriptor((this.tournamentEntryFee ?: 0.0) * 2.0)) } else { data.add(RowRepresentableEditDescriptor(0)) data.add(RowRepresentableEditDescriptor(0)) @@ -596,10 +642,10 @@ open class Session : RealmObject(), SessionInterface, Savable, cgBigBlind = null } SessionRow.BREAK_TIME -> { - val timeFrameToUpdate = - if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) - timeFrameToUpdate.breakDuration = if (value != null) (value as String).toLong() * 60 * 1000 else 0 - timeFrame = timeFrameToUpdate +// val timeFrameToUpdate = +// if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) + this.breakDuration = if (value != null) (value as String).toLong() * 60 * 1000 else 0 +// timeFrame = timeFrameToUpdate } SessionRow.BUY_IN -> { val localResult = if (result != null) result as Result else realm.createObject(Result::class.java) @@ -613,10 +659,12 @@ open class Session : RealmObject(), SessionInterface, Savable, localResult.cashout = null } else { localResult.cashout = (value as String).toDouble() - val timeFrameToUpdate = - if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) - timeFrameToUpdate.setEnd(Date()) - timeFrame = timeFrameToUpdate + this.end() + +// val timeFrameToUpdate = +// if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) +// timeFrameToUpdate.setEnd(Date()) +// timeFrame = timeFrameToUpdate } result = localResult @@ -624,11 +672,13 @@ open class Session : RealmObject(), SessionInterface, Savable, SessionRow.COMMENT -> comment = value as String? ?: "" SessionRow.END_DATE -> if (value is Date?) { - val timeFrameToUpdate = - if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) -// timeFrameToUpdate.setDate(null, value) - timeFrameToUpdate.setEnd(value) - timeFrame = timeFrameToUpdate + this.endDate = value + +// val timeFrameToUpdate = +// if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) +//// timeFrameToUpdate.setDate(null, value) +// timeFrameToUpdate.setEnd(value) +// timeFrame = timeFrameToUpdate } SessionRow.GAME -> { if (value is ArrayList<*>) { @@ -655,16 +705,17 @@ open class Session : RealmObject(), SessionInterface, Savable, localResult.tournamentFinalPosition = if (value == null) null else (value as String).toInt() result = localResult } - SessionRow.START_DATE -> if (value is Date?) { - if (value == null) { - timeFrame = null - } else { - val timeFrameToUpdate = - if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) - timeFrameToUpdate.setStart(value) -// timeFrameToUpdate.setDate(value, null) - timeFrame = timeFrameToUpdate - } + SessionRow.START_DATE -> if (value is Date) { + this.startDate = value +// if (value == null) { +// timeFrame = null +// } else { +// val timeFrameToUpdate = +// if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java) +// timeFrameToUpdate.setStart(value) +//// timeFrameToUpdate.setDate(value, null) +// timeFrame = timeFrameToUpdate +// } } SessionRow.TABLE_SIZE -> tableSize = value as Int? SessionRow.TIPS -> { 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 88b02fb0..23e228bb 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 @@ -5,15 +5,41 @@ import io.realm.RealmObject import io.realm.RealmResults import io.realm.annotations.Ignore import io.realm.annotations.LinkingObjects +import net.pokeranalytics.android.model.interfaces.Timed +import java.util.* -open class SessionSet() : RealmObject() { +open class SessionSet : RealmObject(), Timed { + + override var startDate: Date = Date() + set(value) { + field = value + this.computeDuration() + } + + var endDate: Date = Date() + set(value) { + field = value + this.computeDuration() + } + + override fun endDate(): Date { + return this.endDate + } + + override var breakDuration: Long = 0L + set(value) { + field = value + this.computeDuration() + } + + override var netDuration: Long = 0L companion object { fun newInstance(realm: Realm) : SessionSet { val sessionSet: SessionSet = realm.createObject(SessionSet::class.java) - sessionSet.timeFrame = realm.createObject(TimeFrame::class.java) +// sessionSet.timeFrame = realm.createObject(TimeFrame::class.java) return realm.copyToRealm(sessionSet) } @@ -22,8 +48,7 @@ open class SessionSet() : RealmObject() { /** * The timeframe of the set, i.e. its start & end date */ - - var timeFrame: TimeFrame? = null +// var timeFrame: TimeFrame? = null /** * The list of sessions associated with this set @@ -31,37 +56,17 @@ open class SessionSet() : RealmObject() { @LinkingObjects("sessionSet") val sessions: RealmResults? = null - @Ignore // a netDuration shortcut - var duration: Long = 0L - get() { - return this.timeFrame?.netDuration ?: 0L - } - - @Ignore // a netDuration in hour - var hourlyDuration: Double = 0.0 - get() { - return this.timeFrame?.hourlyDuration ?: 0.0 - } - - @Ignore // a netResult shortcut - var netResult: Double = 0.0 - get () { - return this.sessions?.sumByDouble { it.value } ?: 0.0 - } + @Ignore + val netResult: Double = this.sessions?.sumByDouble { it.value } ?: 0.0 - @Ignore // a netDuration shortcut - var hourlyRate: Double = 0.0 - get () { - return this.netResult / this.hourlyDuration - } + @Ignore + val hourlyRate: Double = this.netResult / this.hourlyDuration @Ignore - var estimatedHands: Double = 25.0 * (this.timeFrame?.hourlyDuration?.toDouble() ?: 0.0) + val estimatedHands: Double = 25.0 * this.hourlyDuration @Ignore var bbNetResult: Double = 0.0 - - } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt index d8f548ea..5b0ce632 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt @@ -1,280 +1,288 @@ -package net.pokeranalytics.android.model.realm - -import io.realm.RealmObject -import io.realm.RealmQuery -import io.realm.RealmResults -import io.realm.annotations.Ignore -import io.realm.annotations.LinkingObjects -import net.pokeranalytics.android.exceptions.ModelException -import timber.log.Timber -import java.util.* - -open class TimeFrame : RealmObject() { - - // A start date - var startDate: Date = Date() - private set(value) { - field = value - this.computeDuration() - } - - // An end date - var endDate: Date? = null - private set(value) { - field = value - this.computeDuration() - } - - // The latest pause date - var pauseDate: Date? = null - set(value) { - field?.let { - if (value == null && field != null) { - breakDuration += Date().time - it.time - } - } - field = value - this.computeDuration() - } - - // The break netDuration - var breakDuration: Long = 0L - set(value) { - field = value - this.computeDuration() - } - - // the total netDuration - var netDuration: Long = 0L - private set - - var hourlyDuration: Double = 0.0 - get() { - return this.netDuration / 3600000.0 // 3.6 millions of milliseconds - } - - // indicates a state of pause - var paused: Boolean = false - - // Session - @LinkingObjects("timeFrame") - private val sessions: RealmResults? = null // we should have only one session - - @Ignore - var session: Session? = null - get() = if (this.sessions != null && this.sessions.isEmpty()) null else this.sessions?.first() - - // Group - @LinkingObjects("timeFrame") - private val sets: RealmResults? = null // we should have only one sessionGroup - - @Ignore - var set: SessionSet? = null - get() = this.sets?.first() - - fun setStart(startDate: Date) { - this.startDate = startDate - this.session?.let { - this.notifySessionDateChange(it) - } - } - - fun setEnd(endDate: Date?) { - this.endDate = endDate - this.session?.let { - this.notifySessionDateChange(it) - } - } - - fun setDate(startDate: Date, endDate: Date?) { - this.startDate = startDate - this.endDate = endDate - - this.session?.let { - this.notifySessionDateChange(it) - } - } - - /** - * Computes the net netDuration of the session - */ - private fun computeDuration() { - var endDate: Date = this.endDate ?: Date() - this.netDuration = endDate.time - this.startDate.time - this.breakDuration - } - - /** - * Queries all time frames that might be impacted by the date change - * Makes all necessary changes to keep sequential time frames - */ - fun notifySessionDateChange(owner: Session) { - - var query: RealmQuery = this.realm.where(SessionSet::class.java) - query.isNotNull("timeFrame") - -// Timber.d("this> sd = : ${this.startDate}, ed = ${this.endDate}") - - val sets = realm.where(SessionSet::class.java).findAll() -// Timber.d("set count = ${sets.size}") - - if (this.endDate == null) { - query.greaterThanOrEqualTo("timeFrame.startDate", this.startDate) - .or() - .greaterThanOrEqualTo("timeFrame.endDate", this.startDate) - .or() - .isNull("timeFrame.endDate") - } else { - val endDate = this.endDate!! - query - .lessThanOrEqualTo("timeFrame.startDate", this.startDate) - .greaterThanOrEqualTo("timeFrame.endDate", this.startDate) - .or() - .lessThanOrEqualTo("timeFrame.startDate", endDate) - .greaterThanOrEqualTo("timeFrame.endDate", endDate) - .or() - .greaterThanOrEqualTo("timeFrame.startDate", this.startDate) - .lessThanOrEqualTo("timeFrame.endDate", endDate) - .or() - .isNull("timeFrame.endDate") - .lessThanOrEqualTo("timeFrame.startDate", endDate) - } - - val sessionGroups = query.findAll() - - this.updateTimeFrames(sessionGroups, owner) - - } - - /** - * Update Time frames from sets - */ - private fun updateTimeFrames(sessionSets: RealmResults, owner: Session) { - - when (sessionSets.size) { - 0 -> this.createSessionGroup(owner) - 1 -> this.updateSessionGroup(owner, sessionSets.first()!!) - else -> this.mergeSessionGroups(owner, sessionSets) - } - - } - - /** - * Creates the session sessionGroup when the session has none - */ - private fun createSessionGroup(owner: Session) { - - val set: SessionSet = SessionSet.newInstance(this.realm) - set.timeFrame?.let { - it.startDate = this.startDate - it.endDate = this.endDate - } ?: run { - throw ModelException("TimeFrame should never be null here") - } - - owner.sessionSet = set - -// Timber.d("sd = : ${set.timeFrame?.startDate}, ed = ${set.timeFrame?.endDate}") - Timber.d("netDuration 1 = : ${set.timeFrame?.netDuration}") - - } - - /** - * Single SessionSet update, the session might be the owner - * Changes the sessionGroup timeframe using the current timeframe dates - */ - private fun updateSessionGroup(owner: Session, sessionSet: SessionSet) { - - var timeFrame: TimeFrame = sessionSet.timeFrame!! // tested in the query -// timeFrame.setDate(this.startDate, this.endDate) - - val sisterSessions = sessionSet.sessions!! // shouldn't crash ever - - // if we have only one session in the set and that it corresponds to the set - if (sessionSet.sessions?.size == 1 && sessionSet.sessions?.first() == owner) { - timeFrame.setDate(this.startDate, this.endDate) - } else { // there are 2+ sessions to manage and possible splits - - val endDate = this.endDate - - // case where all sessions are over but the set is not, we might have a split, so we delete the set and save everything again - if (endDate != null && sisterSessions.all { it.timeFrame?.endDate != null } && timeFrame.endDate == null) { - var sessions = mutableListOf(owner) - sessionSet.sessions?.forEach { sessions.add(it) } - sessionSet.deleteFromRealm() - sessions.forEach { it.timeFrame?.notifySessionDateChange(it) } - } else { - - if (this.startDate.before(timeFrame.startDate)) { - timeFrame.startDate = this.startDate - } - if (endDate != null && timeFrame.endDate != null && endDate.after(timeFrame.endDate)) { - timeFrame.endDate = endDate - } else if (endDate == null) { - timeFrame.endDate = null - } - - owner.sessionSet = sessionSet - -// Timber.d("sd = : ${sessionSet.timeFrame?.startDate}, ed = ${sessionSet.timeFrame?.endDate}") - Timber.d("netDuration 2 = : ${sessionSet.timeFrame?.netDuration}") - } - - } - - } - - /** - * Multiple session sets update: - * Merges all sets into one (delete all then create a new one) - */ - private fun mergeSessionGroups(owner: Session, sessionSets: RealmResults) { - - var startDate: Date = this.startDate - var endDate: Date? = this.endDate - - // find earlier and later dates from all sets - val timeFrames = sessionSets.mapNotNull { it.timeFrame } - timeFrames.forEach { tf -> - if (tf.startDate.before(startDate)) { - startDate = tf.startDate - } - - endDate?.let { ed -> - tf.endDate?.let { tfed -> - if (tfed.after(ed)) { - endDate = tfed - } - } - } ?: run { - endDate = tf.endDate - } - - } - - // get all sessions from sets - var sessions = mutableSetOf() - sessionSets.forEach { set -> - set.sessions?.asIterable()?.let { sessions.addAll(it) } - } - - // delete all sets - sessionSets.deleteAllFromRealm() - - // Create a new sets - val set: SessionSet = SessionSet.newInstance(this.realm) - set.timeFrame?.let { - it.setDate(startDate, endDate) - } ?: run { - throw ModelException("TimeFrame should never be null here") - } - - // Add the session linked to this timeframe to the new sessionGroup - owner.sessionSet = set - - // Add all orphan sessions - sessions.forEach { it.sessionSet = set } - Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") - - } - -} +//package net.pokeranalytics.android.model.realm +// +//import io.realm.RealmObject +//import io.realm.RealmQuery +//import io.realm.RealmResults +//import io.realm.annotations.Ignore +//import io.realm.annotations.LinkingObjects +//import net.pokeranalytics.android.exceptions.ModelException +//import timber.log.Timber +//import java.util.* +// +//open class TimeFrame : RealmObject() { +// +// // A start date +// var startDate: Date = Date() +// private set(value) { +// field = value +// this.computeDuration() +// } +// +// // An end date +// var endDate: Date? = null +// private set(value) { +// field = value +// this.computeDuration() +// } +// +// // The latest pause date +// var pauseDate: Date? = null +// set(value) { +// field?.let { +// if (value == null && field != null) { +// breakDuration += Date().time - it.time +// } +// } +// field = value +// this.computeDuration() +// } +// +// // The break netDuration +// var breakDuration: Long = 0L +// set(value) { +// field = value +// this.computeDuration() +// } +// +// // the total netDuration +// var netDuration: Long = 0L +// private set +// +// var hourlyDuration: Double = 0.0 +// get() { +// return this.netDuration / 3600000.0 // 3.6 millions of milliseconds +// } +// +// // Session +// @LinkingObjects("timeFrame") +// private val sessions: RealmResults? = null // we should have only one session +// +// @Ignore +// var session: Session? = null +// get() = if (this.sessions != null && this.sessions.isEmpty()) null else this.sessions?.first() +// +// // Group +// @LinkingObjects("timeFrame") +// private val sets: RealmResults? = null // we should have only one sessionGroup +// +// @Ignore +// var set: SessionSet? = null +// get() = this.sets?.first() +// +// fun setStart(startDate: Date) { +// this.startDate = startDate +// this.session?.let { +// this.notifySessionDateChange(it) +// } +// } +// +// fun setEnd(endDate: Date?) { +// this.endDate = endDate +// this.session?.let { +// this.notifySessionDateChange(it) +// } +// } +// +// fun setDate(startDate: Date, endDate: Date?) { +// this.startDate = startDate +// this.endDate = endDate +// +// this.session?.let { +// this.notifySessionDateChange(it) +// } +// } +// +// /** +// * Computes the net netDuration of the session +// */ +// private fun computeDuration() { +// var endDate: Date = this.endDate ?: Date() +// this.netDuration = endDate.time - this.startDate.time - this.breakDuration +// } +// +// /** +// * Queries all time frames that might be impacted by the date change +// * Makes all necessary changes to keep sequential time frames +// */ +// fun notifySessionDateChange(owner: Session) { +// +// var query: RealmQuery = this.realm.where(SessionSet::class.java) +// query.isNotNull("timeFrame") +// +//// Timber.d("this> sd = : ${this.startDate}, ed = ${this.endDate}") +// +// val sets = realm.where(SessionSet::class.java).findAll() +//// Timber.d("set count = ${sets.size}") +// +// if (this.endDate == null) { +// query.greaterThanOrEqualTo("timeFrame.startDate", this.startDate) +// .or() +// .greaterThanOrEqualTo("timeFrame.endDate", this.startDate) +// .or() +// .isNull("timeFrame.endDate") +// } else { +// val endDate = this.endDate!! +// query +// .lessThanOrEqualTo("timeFrame.startDate", this.startDate) +// .greaterThanOrEqualTo("timeFrame.endDate", this.startDate) +// .or() +// .lessThanOrEqualTo("timeFrame.startDate", endDate) +// .greaterThanOrEqualTo("timeFrame.endDate", endDate) +// .or() +// .greaterThanOrEqualTo("timeFrame.startDate", this.startDate) +// .lessThanOrEqualTo("timeFrame.endDate", endDate) +// .or() +// .isNull("timeFrame.endDate") +// .lessThanOrEqualTo("timeFrame.startDate", endDate) +// } +// +// val sessionGroups = query.findAll() +// +// this.updateTimeFrames(sessionGroups, owner) +// +// } +// +// /** +// * Update Time frames from sets +// */ +// private fun updateTimeFrames(sessionSets: RealmResults, owner: Session) { +// +// when (sessionSets.size) { +// 0 -> this.createOrUpdateSessionSet(owner) +// 1 -> this.updateSessionGroup(owner, sessionSets.first()!!) +// else -> this.mergeSessionGroups(owner, sessionSets) +// } +// +// } +// +// /** +// * Creates the session sessionGroup when the session has none +// */ +// private fun createOrUpdateSessionSet(owner: Session) { +// +// val set = owner.sessionSet +// if (set != null) { +// set.timeFrame?.startDate = this.startDate +// set.timeFrame?.endDate = this.endDate +// } else { +// this.createSessionSet(owner) +// } +// +//// Timber.d("sd = : ${set.timeFrame?.startDate}, ed = ${set.timeFrame?.endDate}") +// Timber.d("netDuration 1 = : ${set?.timeFrame?.netDuration}") +// +// } +// +// fun createSessionSet(owner: Session) { +// val set: SessionSet = SessionSet.newInstance(this.realm) +// set.timeFrame?.let { +// it.startDate = this.startDate +// it.endDate = this.endDate +// } ?: run { +// throw ModelException("TimeFrame should never be null here") +// } +// +// owner.sessionSet = set +// } +// +// +// /** +// * Single SessionSet update, the session might be the owner +// * Changes the sessionGroup timeframe using the current timeframe dates +// */ +// private fun updateSessionGroup(owner: Session, sessionSet: SessionSet) { +// +// var timeFrame: TimeFrame = sessionSet.timeFrame!! // tested in the query +//// timeFrame.setDate(this.startDate, this.endDate) +// +// val sisterSessions = sessionSet.sessions!! // shouldn't crash ever +// +// // if we have only one session in the set and that it corresponds to the set +// if (sessionSet.sessions?.size == 1 && sessionSet.sessions?.first() == owner) { +// timeFrame.setDate(this.startDate, this.endDate) +// } else { // there are 2+ sessions to manage and possible splits +// +// val endDate = this.endDate +// +// // case where all sessions are over but the set is not, we might have a split, so we delete the set and save everything again +// if (endDate != null && sisterSessions.all { it.timeFrame?.endDate != null } && timeFrame.endDate == null) { +// var sessions = mutableListOf(owner) +// sessionSet.sessions?.forEach { sessions.add(it) } +// sessionSet.deleteFromRealm() +// sessions.forEach { it.timeFrame?.notifySessionDateChange(it) } +// } else { +// +// if (this.startDate.before(timeFrame.startDate)) { +// timeFrame.startDate = this.startDate +// } +// if (endDate != null && timeFrame.endDate != null && endDate.after(timeFrame.endDate)) { +// timeFrame.endDate = endDate +// } else if (endDate == null) { +// timeFrame.endDate = null +// } +// +// owner.sessionSet = sessionSet +// +//// Timber.d("sd = : ${sessionSet.timeFrame?.startDate}, ed = ${sessionSet.timeFrame?.endDate}") +// Timber.d("netDuration 2 = : ${sessionSet.timeFrame?.netDuration}") +// } +// +// } +// +// } +// +// /** +// * Multiple session sets update: +// * Merges all sets into one (delete all then create a new one) +// */ +// private fun mergeSessionGroups(owner: Session, sessionSets: RealmResults) { +// +// var startDate: Date = this.startDate +// var endDate: Date? = this.endDate +// +// // find earlier and later dates from all sets +// val timeFrames = sessionSets.mapNotNull { it.timeFrame } +// timeFrames.forEach { tf -> +// if (tf.startDate.before(startDate)) { +// startDate = tf.startDate +// } +// +// endDate?.let { ed -> +// tf.endDate?.let { tfed -> +// if (tfed.after(ed)) { +// endDate = tfed +// } +// } +// } ?: run { +// endDate = tf.endDate +// } +// +// } +// +// // get all sessions from sets +// var sessions = mutableSetOf() +// sessionSets.forEach { set -> +// set.sessions?.asIterable()?.let { sessions.addAll(it) } +// } +// +// // delete all sets +// sessionSets.deleteAllFromRealm() +// +// // Create a new sets +// val set: SessionSet = SessionSet.newInstance(this.realm) +// set.timeFrame?.let { +// it.setDate(startDate, endDate) +// } ?: run { +// throw ModelException("TimeFrame should never be null here") +// } +// +// // Add the session linked to this timeframe to the new sessionGroup +// owner.sessionSet = set +// +// // Add all orphan sessions +// sessions.forEach { it.sessionSet = set } +// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") +// +// } +// +//} diff --git a/app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt b/app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt index 8dd01732..606c38ef 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt @@ -106,7 +106,7 @@ class FavoriteSessionFinder { lastSessionsQuery.equalTo("location.id", location.id) } val lastSessions = lastSessionsQuery - .sort("timeFrame.startDate", Sort.DESCENDING) + .sort("startDate", Sort.DESCENDING) .limit(FAVORITE_SIGNIFICANT_SESSIONS) .findAll() diff --git a/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt b/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt new file mode 100644 index 00000000..a8a2c624 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt @@ -0,0 +1,140 @@ +package net.pokeranalytics.android.model.utils + +import io.realm.RealmQuery +import io.realm.RealmResults +import net.pokeranalytics.android.model.realm.Session +import net.pokeranalytics.android.model.realm.SessionSet + +class SessionSetManager { + + companion object { + + fun updateTimeline(session: Session) { + + if (!session.realm.isInTransaction) { + throw IllegalStateException("realm should be in transaction at this point") + } + + if (session.endDate == null) { + throw IllegalStateException("End date should never be null here") + } + val endDate = session.endDate!! // tested above + val startDate = session.startDate + + val realm = session.realm + + val query: RealmQuery = realm.where(SessionSet::class.java) + + query + .lessThanOrEqualTo("startDate", startDate) + .greaterThanOrEqualTo("endDate", startDate) + .or() + .lessThanOrEqualTo("startDate", endDate) + .greaterThanOrEqualTo("endDate", endDate) + .or() + .greaterThanOrEqualTo("startDate", startDate) + .lessThanOrEqualTo("endDate", endDate) + + val sessionGroups = query.findAll() + + this.updateTimeFrames(sessionGroups, session) + + } + + /** + * Update Time frames from sets + */ + private fun updateTimeFrames(sessionSets: RealmResults, session: Session) { + + when (sessionSets.size) { + 0 -> this.createOrUpdateSessionSet(session) + else -> this.mergeSessionGroups(session, sessionSets) + } + + } + + private fun createOrUpdateSessionSet(session: Session) { + + val set = session.sessionSet + if (set != null) { + set.startDate = session.startDate + set.endDate = session.endDate!! + } else { + this.createSessionSet(session) + } + + } + + private fun createSessionSet(session: Session) { + val set: SessionSet = SessionSet.newInstance(session.realm) + set.startDate = session.startDate + set.endDate = session.endDate!! + session.sessionSet = set + } + + /** + * Multiple session sets update: + * Merges all sets into one (delete all then create a new one) + */ + private fun mergeSessionGroups(session: Session, sessionSets: RealmResults) { + + var startDate = session.startDate + var endDate = session.endDate!! + + // find earlier and later dates from all sets + sessionSets.forEach { set -> + if (set.startDate.before(startDate)) { + startDate = set.startDate + } + if (set.endDate.after(endDate)) { + endDate = set.endDate + } + } + + // get all sessions from sets + val sessions = mutableSetOf() + sessionSets.forEach { set -> + set.sessions?.asIterable()?.let { sessions.addAll(it) } + } + + // delete all sets + sessionSets.deleteAllFromRealm() + + // Create a new set + val set: SessionSet = SessionSet.newInstance(session.realm) + set.startDate = startDate + set.endDate = endDate + + // Add the session linked to this timeframe to the new sessionGroup + session.sessionSet = set + + // Add all orphan sessions + sessions.forEach { it.sessionSet = set } +// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") + + } + + fun removeFromTimeline(session: Session) { + + if (!session.realm.isInTransaction) { + throw IllegalStateException("realm should be in transaction at this point") + } + + val sessionSet = session.sessionSet + if (sessionSet != null) { + + val sessions = mutableSetOf() + sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) } + sessions.remove(session) + + sessionSet.deleteFromRealm() + + sessions.forEach { + SessionSetManager.updateTimeline(it) + } + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt index 68684f18..94278d6d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt @@ -90,13 +90,13 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott requireContext(), row, this, - currentSession.timeFrame?.startDate ?: Date() + currentSession.startDate ) SessionRow.END_DATE -> DateTimePickerManager.create( requireContext(), row, this, - currentSession.timeFrame?.endDate ?: currentSession.timeFrame?.startDate ?: Date() + currentSession.endDate ?: currentSession.startDate ?: Date() ) SessionRow.BANKROLL -> { BottomSheetFragment.create(fragmentManager, row, this, data, false) 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 6e9d00bf..db112820 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 @@ -16,7 +16,6 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.fragment.components.SessionObserverFragment import net.pokeranalytics.android.ui.view.RowRepresentable -import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable import net.pokeranalytics.android.util.NULL_TEXT @@ -111,7 +110,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc } } - val allStats: List = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_DURATION, Stat.DURATION) + val allStats: List = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NUMBER_OF_SETS, Stat.AVERAGE_DURATION, Stat.DURATION) val allSessionGroup = SessionGroup(getString(R.string.all), super.sessions, allStats) val cgStats: List = 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) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/SessionObserverFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/SessionObserverFragment.kt index 0f5bd592..0aff17c0 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/SessionObserverFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/SessionObserverFragment.kt @@ -10,7 +10,7 @@ open class SessionObserverFragment : PokerAnalyticsFragment() { init { val realm = Realm.getDefaultInstance() - this.sessions = realm.where(Session::class.java).isNotNull("timeFrame.endDate").findAll() + this.sessions = realm.where(Session::class.java).isNotNull("endDate").findAll() this.sessions.addChangeListener { _, _ -> this.sessionsChanged() } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt index 65549184..ffc14407 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt @@ -95,11 +95,13 @@ class SessionRowView : FrameLayout { rowHistorySession.sessionTitle.text = title // Duration - rowHistorySession.sessionInfoDurationIcon.isVisible = session.timeFrame != null - rowHistorySession.sessionInfoDurationValue.isVisible = session.timeFrame != null - session.timeFrame?.let { - rowHistorySession.sessionInfoDurationValue.text = session.getDuration() - } +// rowHistorySession.sessionInfoDurationIcon.isVisible = session.timeFrame != null +// rowHistorySession.sessionInfoDurationValue.isVisible = session.timeFrame != null +// session.timeFrame?.let { +// rowHistorySession.sessionInfoDurationValue.text = session.getDuration() +// } + + rowHistorySession.sessionInfoDurationValue.text = session.getDuration() // Location rowHistorySession.sessionInfoLocationIcon.isVisible = session.location != null @@ -127,7 +129,7 @@ class SessionRowView : FrameLayout { rowHistorySession.infoIcon.isVisible = true rowHistorySession.infoIcon.setImageResource(R.drawable.ic_planned) rowHistorySession.infoTitle.isVisible = true - rowHistorySession.infoTitle.text = session.timeFrame?.startDate?.shortTime() + rowHistorySession.infoTitle.text = session.startDate.shortTime() } else { rowHistorySession.gameResult.isVisible = true rowHistorySession.infoIcon.isVisible = false diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c67cb572..eec3e5c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,7 +8,7 @@ Hourly Rate - Number of groups + Number of sessions Number of games Average duration Net(BB) per 100 hands