diff --git a/app/src/androidTest/java/net/pokeranalytics/android/BankrollInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/BankrollInstrumentedUnitTest.kt new file mode 100644 index 00000000..f2bb163b --- /dev/null +++ b/app/src/androidTest/java/net/pokeranalytics/android/BankrollInstrumentedUnitTest.kt @@ -0,0 +1,82 @@ +package net.pokeranalytics.android + +import androidx.test.ext.junit.runners.AndroidJUnit4 +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.Bankroll +import net.pokeranalytics.android.model.realm.Currency +import net.pokeranalytics.android.model.realm.Session +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import java.util.* + +@RunWith(AndroidJUnit4::class) +class BankrollInstrumentedUnitTest : 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") + + val br1 = realm.createObject(Bankroll::class.java, "1") + val br2 = realm.createObject(Bankroll::class.java, "2") + + val c1 = realm.createObject(Currency::class.java, "1") + val c2 = realm.createObject(Currency::class.java, "2") + c1.rate = 0.1 + c2.rate = 2.0 + br1.currency = c1 + br2.currency = c2 + + s1.bankroll = br1 + s2.bankroll = br2 + + 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?.netResult = 100.0 + s2.result?.netResult = 200.0 + + realm.commitTransaction() + + val sessions = realm.where(Session::class.java).findAll() + val group = SessionGroup(name = "test", sessions = sessions) + + val options = Calculator.Options() + + val results: ComputedResults = Calculator.compute(group, options) + val delta = 0.01 + + val sum = results.computedStat(Stat.NETRESULT) + if (sum != null) { + Assert.assertEquals(410.0, sum.value, delta) + } else { + Assert.fail("No Net result stat") + } + + val average = results.computedStat(Stat.AVERAGE) + if (average != null) { + Assert.assertEquals(205.0, average.value, delta) + } else { + Assert.fail("No AVERAGE stat") + } + + } + +} \ No newline at end of file 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/FavoriteSessionUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/FavoriteSessionUnitTest.kt index e3a6343c..8ecfd436 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/FavoriteSessionUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/FavoriteSessionUnitTest.kt @@ -32,7 +32,7 @@ class FavoriteSessionUnitTest : RealmInstrumentedUnitTest() { realm.insert(s3) realm.commitTransaction() - val favSession = FavoriteSessionFinder.favoriteSession(Session.Type.CASH_GAME, null, realm, InstrumentationRegistry.getInstrumentation().targetContext) + val favSession = FavoriteSessionFinder.favoriteSession(Session.Type.CASH_GAME.ordinal, null, realm, InstrumentationRegistry.getInstrumentation().targetContext) if (favSession != null) { Assert.assertEquals(4.0, favSession.cgBigBlind) @@ -68,7 +68,7 @@ class FavoriteSessionUnitTest : RealmInstrumentedUnitTest() { realm.insert(s3) realm.commitTransaction() - val favSession = FavoriteSessionFinder.favoriteSession(Session.Type.CASH_GAME, loc2, realm, InstrumentationRegistry.getInstrumentation().targetContext) + val favSession = FavoriteSessionFinder.favoriteSession(Session.Type.CASH_GAME.ordinal, loc2, realm, InstrumentationRegistry.getInstrumentation().targetContext) if (favSession != null) { Assert.assertEquals(1.0, favSession.cgBigBlind) 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 aa58458b..97b0fde8 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -16,7 +16,7 @@ import java.util.* class PokerAnalyticsApplication : Application() { - var sessions: RealmResults? = null + var sessions: RealmResults? = null // private val listener: OrderedRealmCollectionChangeListener> = // OrderedRealmCollectionChangeListener() { realmResults: RealmResults, changeSet: OrderedCollectionChangeSet -> @@ -32,162 +32,171 @@ class PokerAnalyticsApplication : Application() { // // } - override fun onCreate() { - super.onCreate() - - // Realm - Realm.init(this) - val realmConfiguration = RealmConfiguration.Builder() - .name(Realm.DEFAULT_REALM_NAME) - .deleteRealmIfMigrationNeeded() - .build() - Realm.setDefaultConfiguration(realmConfiguration) - - val realm: Realm = Realm.getDefaultInstance() - // Add observer on session time frame changes - 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() } - */ + override fun onCreate() { + super.onCreate() + + // Realm + Realm.init(this) + val realmConfiguration = RealmConfiguration.Builder() + .name(Realm.DEFAULT_REALM_NAME) + .deleteRealmIfMigrationNeeded() + .build() + Realm.setDefaultConfiguration(realmConfiguration) + + val realm: Realm = Realm.getDefaultInstance() + // Add observer on session time frame changes + this.sessions = realm.where(Session::class.java).findAll() // monitor session deletions +// this.endedSessions?.addChangeListener { _, changeSet -> + /* + val deletedSessions = + realm.where(Session::class.java).`in`("id", changeSet.deletions.toTypedArray()).findAll() + deletedSessions.forEach { it.cleanup() } + */ // } - if (BuildConfig.DEBUG) { - // Logs - Timber.plant(PokerAnalyticsLogs()) - } else { - //Fabric.with(this, Crashlytics()) - } - - if (BuildConfig.DEBUG) { - createDefaultData() - } - - } - - /** - * Create default data - */ - private fun createDefaultData() { - - val realm = Realm.getDefaultInstance() - - // Games - val gamesName = resources.getStringArray(R.array.game_name) - val gamesShortName = resources.getStringArray(R.array.game_short_name) - if (realm.where().findAll().isEmpty()) { - realm.executeTransaction { - for ((index, name) in gamesName.withIndex()) { - val game = Game() - game.id = UUID.randomUUID().toString() - game.name = name - game.shortName = gamesShortName[index] - realm.copyToRealmOrUpdate(game) - } - } - } - - // Tournament types - val tournamentFakeName = resources.getStringArray(R.array.tournament_fake_name) - if (realm.where().findAll().isEmpty()) { - realm.executeTransaction { - for (type in tournamentFakeName) { - val tournament = TournamentName() - tournament.id = UUID.randomUUID().toString() - tournament.name = type - realm.copyToRealmOrUpdate(tournament) - } - } - } - - // Currency - if (realm.where().findAll().isEmpty()) { - realm.executeTransaction { - val localeCurrency = java.util.Currency.getInstance(Locale.getDefault()) - val defaultCurrency = Currency() - defaultCurrency.code = localeCurrency.currencyCode - realm.copyToRealmOrUpdate(defaultCurrency) - } - } - - // Bankroll - if (realm.where().findAll().isEmpty()) { - realm.executeTransaction { - val localeCurrency = java.util.Currency.getInstance(Preferences.getCurrencyLocale(this)) - val bankroll = Bankroll() - bankroll.name = "Live" - bankroll.live = true - bankroll.currency = realm.where().equalTo("code", localeCurrency.currencyCode).findFirst() - realm.copyToRealmOrUpdate(bankroll) - } - } - - // Test sessions - if (BuildConfig.DEBUG) { - val sessions = realm.where().findAll() - if (sessions.size < 10) { - for (index in 0..50) { - - realm.executeTransaction { - val session = Session.newInstance(realm, false) + if (BuildConfig.DEBUG) { + // Logs + Timber.plant(PokerAnalyticsLogs()) + } else { + //Fabric.with(this, Crashlytics()) + } + + this.createDefaultData() + if (BuildConfig.DEBUG) { +// this.createFakeSessions() // debug + } + + } + + /** + * Create default data + */ + private fun createDefaultData() { + + val realm = Realm.getDefaultInstance() + + // Games + val gamesName = resources.getStringArray(R.array.game_name) + val gamesShortName = resources.getStringArray(R.array.game_short_name) + if (realm.where().findAll().isEmpty()) { + realm.executeTransaction { + for ((index, name) in gamesName.withIndex()) { + val game = Game() + game.id = UUID.randomUUID().toString() + game.name = name + game.shortName = gamesShortName[index] + realm.copyToRealmOrUpdate(game) + } + } + } + + // Tournament types + val tournamentFakeName = resources.getStringArray(R.array.tournament_fake_name) + if (realm.where().findAll().isEmpty()) { + realm.executeTransaction { + for (type in tournamentFakeName) { + val tournament = TournamentName() + tournament.id = UUID.randomUUID().toString() + tournament.name = type + realm.copyToRealmOrUpdate(tournament) + } + } + } + + // Currency + if (realm.where().findAll().isEmpty()) { + realm.executeTransaction { + val localeCurrency = java.util.Currency.getInstance(Locale.getDefault()) + val defaultCurrency = Currency() + defaultCurrency.code = localeCurrency.currencyCode + realm.copyToRealmOrUpdate(defaultCurrency) + } + } + + // Bankroll + if (realm.where().findAll().isEmpty()) { + realm.executeTransaction { + val localeCurrency = java.util.Currency.getInstance(Preferences.getCurrencyLocale(this)) + val bankroll = Bankroll() + bankroll.name = "Live" + bankroll.live = true + bankroll.currency = realm.where().equalTo("code", localeCurrency.currencyCode).findFirst() + realm.copyToRealmOrUpdate(bankroll) + } + } + + realm.close() + + } + + private fun createFakeSessions() { + + val realm = Realm.getDefaultInstance() + + // Test endedSessions + val sessions = realm.where().findAll() + if (sessions.size < 10) { + for (index in 0..50) { + + realm.executeTransaction { + val session = Session.newInstance(realm, false) // session.id = UUID.randomUUID().toString() - val calendar = Calendar.getInstance() - calendar.set( - (2017..2018).random(), - (0..11).random(), - (0..28).random(), - (0..23).random(), - (0..59).random() - ) + val calendar = Calendar.getInstance() + calendar.set( + (2017..2018).random(), + (0..11).random(), + (0..28).random(), + (0..23).random(), + (0..59).random() + ) - val startDate = calendar.time - calendar.add(Calendar.HOUR_OF_DAY, (2..12).random()) - calendar.add(Calendar.MINUTE, (0..59).random()) - val endDate = calendar.time + val startDate = calendar.time + calendar.add(Calendar.HOUR_OF_DAY, (2..12).random()) + calendar.add(Calendar.MINUTE, (0..59).random()) + val endDate = calendar.time // val timeFrame = TimeFrame() - session.timeFrame?.let { -// it.startDate = startDate -// it.endDate = endDate - it.setDate(startDate, endDate) - } + session.startDate = startDate + session.endDate = endDate -// session.timeFrame = timeFrame - session.creationDate = startDate +// session.timeFrame?.let { +// // it.startDate = startDate +//// it.endDate = endDate +// it.setDate(startDate, endDate) +// } - session.limit = Limit.values().random().ordinal - session.game = realm.where().findAll().random() +// session.timeFrame = timeFrame + session.creationDate = startDate - session.result?.let { result -> - result.buyin = arrayListOf(100, 200, 300, 500, 1000, 2000).random().toDouble() - result.netResult = arrayListOf( - -2500.0, -2000.0, -1500.0, -1000.0, -500.0, 200.0, 1000.0, 1500.0, - 2500.0 - ).random() - } + session.limit = Limit.values().random().ordinal + session.game = realm.where().findAll().random() - realm.copyToRealmOrUpdate(session) - } - } - } - } + session.result?.let { result -> + result.buyin = arrayListOf(100, 200, 300, 500, 1000, 2000).random().toDouble() + result.netResult = arrayListOf( + -2500.0, -2000.0, -1500.0, -1000.0, -500.0, 200.0, 1000.0, 1500.0, + 2500.0 + ).random() + } - realm.close() + realm.copyToRealmOrUpdate(session) + } + } + } - val sets = realm.where(SessionSet::class.java).findAll() +// val sets = realm.where(SessionSet::class.java).findAll() +// // Timber.d("sets = ${sets.size}") // // sets.forEach { set -> // Timber.d("set sd = : ${set.timeFrame?.startDate}, ed = ${set.timeFrame?.endDate}") // } + realm.close() - - } + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt index f7ed7486..d08994f1 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 @@ -106,7 +106,7 @@ class Calculator { sessions.forEach { s -> index++; - sum += s.value + sum += s.ratedNet bbSum += s.bbNetResult bbSessionCount += s.bigBlindSessionCount if (s.value >= 0) { @@ -147,7 +147,7 @@ class Calculator { sessionSets.forEach { sessionSet -> gIndex++ duration += sessionSet.hourlyDuration - gSum += sessionSet.netResult + gSum += sessionSet.ratedNet gTotalHands += sessionSet.estimatedHands gBBSum += sessionSet.bbNetResult @@ -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), @@ -219,7 +224,7 @@ class Calculator { var stdSum: Double = 0.0 var stdBBper100HandsSum: Double = 0.0 sessions.forEach { session -> - stdSum += Math.pow(session.value - average, 2.0) + stdSum += Math.pow(session.ratedNet - average, 2.0) stdBBper100HandsSum += Math.pow(session.bbPer100Hands - bbPer100Hands, 2.0) } val standardDeviation: Double = Math.sqrt(stdSum / sessions.size) 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..c47a2b7f 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,7 @@ interface SessionInterface : Summable { var bigBlindSessionCount: Int // 0 or 1 var buyin: Double var bbPer100Hands: Double - + var ratedNet: Double } /** @@ -33,7 +33,7 @@ class SessionGroup(name: String, sessions: List, stats: List = sessions diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt index 423b7641..ce774dac 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -37,9 +37,9 @@ enum class Stat : RowRepresentable { * Returns whether the stat evolution values requires a distribution sorting */ fun hasDistributionSorting() : Boolean { - when (this) { - STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> return true - else -> return false + return when (this) { + STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> true + else -> false } } @@ -146,8 +146,8 @@ class ComputedStat(stat: Stat, value: Double) { return TextFormat(value.formattedHourlyDuration()) } // red/green percentages Stat.WIN_RATIO, Stat.ROI -> { - val color = if (value >= this.stat.threshold) R.color.green else R.color.red - return TextFormat("${value.formatted()}%", color) + val color = if (value * 100 >= this.stat.threshold) R.color.green else R.color.red + return TextFormat("${(value * 100).formatted()}%", color) } // white amounts Stat.AVERAGE_BUYIN, Stat.STANDARD_DEVIATION, Stat.STANDARD_DEVIATION_HOURLY, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> { diff --git a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt index 3f6d1e29..c3751f9c 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -6,4 +6,8 @@ class ModelException(message: String) : Exception(message) { class FormattingException(message: String) : Exception(message) { +} + +class TypeException(message: String) : Exception(message) { + } \ No newline at end of file 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..5b335cfa 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 @@ -8,8 +8,15 @@ enum class SessionState { PLANNED, STARTED, PAUSED, - FINISHED, - INVALID; + FINISHED; + + var hasStarted: Boolean = false + get() { + return when (this) { + STARTED, PAUSED, FINISHED -> true + else -> false + } + } } /** @@ -18,24 +25,22 @@ enum class SessionState { */ fun Session.getState(): SessionState { - if (timeFrame == null) { +// if (timeFrame == null) { +// return SessionState.PENDING +// } + val start = this.startDate + if (start == 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 - } + } else { + if (start > Date()) { + return SessionState.PLANNED + } else if (this.endDate != null) { + return SessionState.FINISHED + } else if (this.pauseDate != null) { + 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..7803b4f1 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt @@ -0,0 +1,33 @@ +package net.pokeranalytics.android.model.interfaces + +import java.util.* + +interface Timed { + + fun startDate() : Date? + + fun endDate() : Date + + var breakDuration: Long + + var pauseDate: Date? + + var netDuration: Long + + + /** + * Computes the net netDuration of the session + */ + fun computeNetDuration() { + this.startDate()?.let { start -> + this.netDuration = this.endDate().time - start.time - this.breakDuration + } ?: run { + this.netDuration = 0L + } + } + + 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/Bankroll.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt index 5504d3ad..83337bbe 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt @@ -37,8 +37,6 @@ open class Bankroll(name: String = "") : RealmObject(), Savable, // The currency of the bankroll var currency: Currency? = null - // @todo rate management - override fun getDisplayName(): String { return this.name } @@ -65,7 +63,7 @@ open class Bankroll(name: String = "") : RealmObject(), Savable, override fun boolForRow(row: RowRepresentable): Boolean { when (row) { - BankrollRow.LIVE -> return this.live + BankrollRow.LIVE -> return !this.live else -> return super.boolForRow(row) } } @@ -86,8 +84,9 @@ open class Bankroll(name: String = "") : RealmObject(), Savable, override fun updateValue(value: Any?, row: RowRepresentable) { when (row) { SimpleRow.NAME -> this.name = value as String? ?: "" - BankrollRow.LIVE -> this.live = value as Boolean? ?: false - + BankrollRow.LIVE -> { + this.live = if (value is Boolean) !value else false + } } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt index f9cf1250..501d43dc 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt @@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.realm import com.google.android.libraries.places.api.model.Place import io.realm.RealmObject +import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey import net.pokeranalytics.android.model.interfaces.Savable import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource @@ -30,6 +31,9 @@ open class Location : RealmObject(), Savable, StaticRowRepresentableDataSource, // the latitude of the location var latitude: Double? = null + @Ignore + var isLookingForPlaces = false + override fun getDisplayName(): String { return this.name } @@ -53,6 +57,13 @@ open class Location : RealmObject(), Savable, StaticRowRepresentableDataSource, } } + override fun boolForRow(row: RowRepresentable): Boolean { + return when(row) { + LocationRow.LOCATE_ME -> return isLookingForPlaces + else -> super.boolForRow(row) + } + } + override fun editDescriptors(row: RowRepresentable): ArrayList { val data = java.util.ArrayList() when (row) { @@ -78,6 +89,7 @@ open class Location : RealmObject(), Savable, StaticRowRepresentableDataSource, SimpleRow.NAME -> this.name = value as String? ?: "" LocationRow.ADDRESS -> this.address = value as String? ?: "" LocationRow.LOCATE_ME -> { + isLookingForPlaces = false if (value is Place) { setPlace(value) } 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 2c992301..845f5819 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 @@ -12,6 +12,7 @@ import net.pokeranalytics.android.R import net.pokeranalytics.android.calculus.ComputedStat import net.pokeranalytics.android.calculus.SessionInterface import net.pokeranalytics.android.calculus.Stat +import net.pokeranalytics.android.exceptions.ModelException import net.pokeranalytics.android.model.Limit import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.TableSize @@ -19,7 +20,10 @@ import net.pokeranalytics.android.model.TournamentKind 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 import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowViewType @@ -28,10 +32,11 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRowRepresent import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow import net.pokeranalytics.android.util.* import java.util.* +import java.util.Currency 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, @@ -59,14 +64,59 @@ 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 + + /** + * The start date of the session + */ + var startDate: Date? = null set(value) { field = value -// value?.let { it.notifySessionDateChange(this) } + this.computeNetDuration() + // nullifies endate when setting the start date after the end date + if (value != null && this.endDate != null && value.after(this.endDate)) { + this.endDate = null + } + this.dateChanged() + } + + /** + * the end date of the session + */ + var endDate: Date? = null + set(value) { + field = value + this.computeNetDuration() + this.dateChanged() + } + + /** + * The break duration of the session + */ + override var breakDuration: Long = 0L + set(value) { + field = value + this.computeNetDuration() } - // The time frame sessionGroup, which can contain multiple sessions + /** + * the net duration of the session, automatically calculated + */ + override var netDuration: Long = 0L + + /** + * The start date of the break + */ + override 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 endedSessions override var sessionSet: SessionSet? = null // the date of creation of the app @@ -124,20 +174,110 @@ open class Session : RealmObject(), SessionInterface, Savable, // The features of the tournament, like Knockout, Shootout, Turbo... var tournamentFeatures: RealmList = RealmList() + /** + * Manages impacts on SessionSets + * Should be called when the start / end date are changed + */ + private fun dateChanged() { + if (this.endDate != null) { + SessionSetManager.updateTimeline(this) + } else if (this.sessionSet != null) { + SessionSetManager.removeFromTimeline(this) + } + } + + /** + * Returns a non-null date for the session + */ + override fun endDate(): Date { + return this.endDate ?: Date() + } + + /** + * Returns the nullable date + */ + override fun startDate(): Date? { + return this.startDate + } + /** * 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() { + return this.result?.net ?: 0.0 + } + + @Ignore + override var estimatedHands: Double = 25.0 * this.hourlyDuration + + @Ignore + override var bbNetResult: Double = 0.0 + get() { + this.cgBigBlind?.let { bb -> + this.result?.let { result -> + return result.net / bb + } + } + return 0.0 + } + + @Ignore + override var bbPer100Hands: Double = 0.0 + get() { + return this.bbNetResult / this.estimatedHands * 100.0 + } + + @Ignore + override var bigBlindSessionCount: Int = if (this.cgBigBlind != null) 1 else 0 + + @Ignore + override var buyin: Double = 0.0 + get() { + this.result?.let { result -> + result.buyin?.let { + return it + } + } + return 0.0 + } + + val hourlyRate: Double + get() { + this.result?.let { result -> + return result.net / this.hourlyDuration + } + throw ModelException("Session should have an existing Result relationship") + } + + override var ratedNet: Double = 0.0 + get() { + this.result?.net?.let { net -> + this.bankroll?.currency?.rate?.let { rate -> + return net * rate + } ?: run { + return net + } + } + return 0.0 + } + + // States + /** * Start or continue a session */ @@ -145,16 +285,22 @@ 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()) -// sessionTimeFrame.setDate(Date(), null) - this.timeFrame = sessionTimeFrame + this.startDate = Date() + if (this.tournamentEntryFee != null) { + this.result?.buyin = this.tournamentEntryFee + } } SessionState.PAUSED -> { - this.timeFrame?.paused = false - this.timeFrame?.pauseDate = null + val pauseDate = this.pauseDate + if (pauseDate != null) { + this.breakDuration += Date().time - pauseDate.time + } else { + throw IllegalStateException("When resuming, the pause date must be set") + } + this.pauseDate = null } else -> { + throw IllegalStateException("unmanaged session state") } } } @@ -167,9 +313,9 @@ open class Session : RealmObject(), SessionInterface, Savable, realm.executeTransaction { when (getState()) { SessionState.STARTED -> { - this.timeFrame?.paused = true - this.timeFrame?.pauseDate = Date() + this.pauseDate = Date() } + else -> throw IllegalStateException("Pausing a session in an unmanaged state") } } } @@ -181,10 +327,7 @@ 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.timeFrame?.setDate(null, Date()) + this.end() } else -> throw Exception("Stopping session in unmanaged state") } @@ -196,27 +339,43 @@ 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 } } + /** + * Utility method to cleanly end a session + */ + private fun end() { + this.pauseDate = null + if (this.endDate == null) { + this.endDate = Date() + } + } + + // Formatters + /** * Return the netDuration of the current session */ - fun getDuration(): String { - val startDate = timeFrame?.startDate ?: Date() - val enDate = timeFrame?.endDate ?: Date() - return startDate.getDuration(enDate) + fun getFormattedDuration(): String { + this.startDate?.let { start -> + val netDuration = this.endDate().time - start.time - this.breakDuration + return netDuration.toMinutes() + } + return NULL_TEXT } /** * Return the formatted blinds */ fun getBlinds(): String { - return if (cgSmallBlind == null) NULL_TEXT else "${cgSmallBlind?.toCurrency()}/${cgBigBlind?.round()}" + val currencyCode = bankroll?.currency?.code ?: Currency.getInstance(Locale.getDefault()).currencyCode + val currencySymbol = Currency.getInstance(currencyCode).symbol + return if (cgSmallBlind == null) NULL_TEXT else "$currencySymbol ${cgSmallBlind?.formatted()}/${cgBigBlind?.round()}" } /** @@ -233,6 +392,7 @@ open class Session : RealmObject(), SessionInterface, Savable, return if (gameTitle.isNotBlank()) gameTitle else NULL_TEXT } + // LifeCycle /** * Delete the object from realm @@ -252,62 +412,15 @@ open class Session : RealmObject(), SessionInterface, Savable, this.sessionSet?.let { set -> - // get all sessions part of the deleted session set - val sessionsFromSet = set.sessions - // cleanup unnecessary related objects set.deleteFromRealm() - this.timeFrame?.deleteFromRealm() this.result?.deleteFromRealm() - // make sessions recreate/find their session set - sessionsFromSet?.let { sessions -> - sessions.forEach { session -> - session.timeFrame?.notifySessionDateChange(session) - } - } - } - } - - @Ignore // SessionInterface value - override var value: Double = 0.0 - get() { - return this.result?.net ?: 0.0 - } - - @Ignore - override var estimatedHands: Double = 25.0 * (this.timeFrame?.hourlyDuration ?: 0.0) - - @Ignore - override var bbNetResult: Double = 0.0 - get() { - this.cgBigBlind?.let { bb -> - this.result?.let { result -> - return result.net / bb - } - } - return 0.0 - } - - @Ignore - override var bbPer100Hands: Double = 0.0 - get() { - return this.bbNetResult / this.estimatedHands * 100.0 - } - - @Ignore - override var bigBlindSessionCount: Int = if (this.cgBigBlind != null) 1 else 0 + // Updates the timeline + SessionSetManager.removeFromTimeline(this) - @Ignore - override var buyin: Double = 0.0 - get() { - this.result?.let { - it.buyin?.let { - return it - } - } - return 0.0 } + } @Ignore override val viewType: Int = RowViewType.ROW_SESSION.ordinal @@ -329,7 +442,7 @@ open class Session : RealmObject(), SessionInterface, Savable, rows.add( HeaderRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT_BIG, - title = getDuration(), + title = getFormattedDuration(), computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) ) ) @@ -349,7 +462,7 @@ open class Session : RealmObject(), SessionInterface, Savable, rows.add( HeaderRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT_BIG, - title = getDuration(), + title = getFormattedDuration(), computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) ) ) @@ -357,7 +470,7 @@ open class Session : RealmObject(), SessionInterface, Savable, HeaderRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT, resId = R.string.hour_rate_without_pauses, - computedStat = ComputedStat(Stat.NETRESULT, this.sessionSet?.hourlyRate ?: 0.0) + computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate) ) ) @@ -393,19 +506,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.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.BREAK_TIME -> if (this.breakDuration > 0.0) this.breakDuration.toMinutes() else NULL_TEXT + SessionRow.BUY_IN -> this.result?.buyin?.toCurrency() ?: NULL_TEXT + SessionRow.CASHED_OUT, SessionRow.PRIZE, SessionRow.NET_RESULT -> this.result?.cashout?.toCurrency() ?: NULL_TEXT + SessionRow.COMMENT -> if (this.comment.isNotEmpty()) this.comment 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() ?: NULL_TEXT SessionRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT SessionRow.TIPS -> result?.tips?.toCurrency() ?: NULL_TEXT SessionRow.TOURNAMENT_KIND -> this.tournamentKind?.let { @@ -419,8 +530,7 @@ open class Session : RealmObject(), SessionInterface, Savable, } } SessionRow.TOURNAMENT_NAME -> tournamentName?.name ?: NULL_TEXT - else -> NULL_TEXT - + else -> throw UnmanagedRowRepresentableException("Unmanaged row = ${row.getDisplayName()}") } } @@ -468,12 +578,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)) @@ -593,10 +703,7 @@ 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 + this.breakDuration = if (value != null) (value as String).toLong() * 60 * 1000 else 0 } SessionRow.BUY_IN -> { val localResult = if (result != null) result as Result else realm.createObject(Result::class.java) @@ -610,10 +717,7 @@ 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() } result = localResult @@ -621,11 +725,8 @@ 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 + } SessionRow.GAME -> { if (value is ArrayList<*>) { @@ -652,16 +753,8 @@ 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 } 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..b2bed1e7 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,53 @@ 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 { + + var startDate: Date = Date() + set(value) { + field = value + this.computeNetDuration() + } + + var endDate: Date = Date() + set(value) { + field = value + this.computeNetDuration() + } + + override fun startDate(): Date? { + return this.startDate + } + + override fun endDate(): Date { + return this.endDate + } + + override var breakDuration: Long = 0L + set(value) { + field = value + this.computeNetDuration() + } + + /** + * The start date of the break + */ + override var pauseDate: Date? = null + + /** + * the net duration of the set + */ + 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,46 +60,25 @@ 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 + * The list of endedSessions associated with this set */ @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 // a netDuration shortcut - var hourlyRate: Double = 0.0 - get () { - return this.netResult / this.hourlyDuration - } - @Ignore - var estimatedHands: Double = 25.0 * (this.timeFrame?.hourlyDuration?.toDouble() ?: 0.0) + val ratedNet: Double = this.sessions?.sumByDouble { it.ratedNet } ?: 0.0 @Ignore - var bbNetResult: Double = 0.0 + val hourlyRate: Double = this.ratedNet / this.hourlyDuration + @Ignore + val estimatedHands: Double = 25.0 * this.hourlyDuration + @Ignore + var bbNetResult: Double = this.sessions?.sumByDouble { it.bbNetResult } ?: 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..f6970a25 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.computeNetDuration() +// } +// +// // An end date +// var endDate: Date? = null +// private set(value) { +// field = value +// this.computeNetDuration() +// } +// +// // 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.computeNetDuration() +// } +// +// // The break netDuration +// var breakDuration: Long = 0L +// set(value) { +// field = value +// this.computeNetDuration() +// } +// +// // 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 endedSessions: RealmResults? = null // we should have only one session +// +// @Ignore +// var session: Session? = null +// get() = if (this.endedSessions != null && this.endedSessions.isEmpty()) null else this.endedSessions?.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 computeNetDuration() { +// 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.endedSessions!! // shouldn't crash ever +// +// // if we have only one session in the set and that it corresponds to the set +// if (sessionSet.endedSessions?.size == 1 && sessionSet.endedSessions?.first() == owner) { +// timeFrame.setDate(this.startDate, this.endDate) +// } else { // there are 2+ endedSessions to manage and possible splits +// +// val endDate = this.endDate +// +// // case where all endedSessions 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 endedSessions = mutableListOf(owner) +// sessionSet.endedSessions?.forEach { endedSessions.add(it) } +// sessionSet.deleteFromRealm() +// endedSessions.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 endedSessions from sets +// var endedSessions = mutableSetOf() +// sessionSets.forEach { set -> +// set.endedSessions?.asIterable()?.let { endedSessions.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 endedSessions +// endedSessions.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 3a262e34..0ddb035e 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 @@ -99,14 +99,14 @@ class FavoriteSessionFinder { /** * Determines the favorite session given a [sessionType] and an optional [location] */ - private fun favoriteSession(sessionType: Int, location: Location?, realm: Realm, context: Context) : Session? { + fun favoriteSession(sessionType: Int, location: Location?, realm: Realm, context: Context) : Session? { val lastSessionsQuery = realm.where(Session::class.java).equalTo("type", sessionType) if (location != null) { 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..f68ccccf --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt @@ -0,0 +1,161 @@ +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 +import kotlin.math.max + +/** + * The manager is in charge of updating the abstract concept of timeline, + * representing the sequenced time frames where the user plays. + */ +class SessionSetManager { + + companion object { + + /** + * Updates the global timeline using the updated [session] + */ + fun updateTimeline(session: Session) { + + if (!session.realm.isInTransaction) { + throw IllegalStateException("realm should be in transaction at this point") + } + + if (session.startDate == null || session.endDate == null) { + throw IllegalStateException("Start or 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 the global timeline using the impacted [sessionSets] and the updated [session] + */ + private fun updateTimeFrames(sessionSets: RealmResults, session: Session) { + + when (sessionSets.size) { + 0 -> this.createOrUpdateSessionSet(session) + else -> this.mergeSessionGroups(session, sessionSets) + } + + } + + /** + * Creates or update the session set for the [session] + */ + private fun createOrUpdateSessionSet(session: Session) { + + val set = session.sessionSet + if (set != null) { + set.startDate = session.startDate!! // tested above + set.endDate = session.endDate!! + } else { + this.createSessionSet(session) + } + + } + + /** + * Create a set and affect it to the [session] + */ + private fun createSessionSet(session: Session) { + val set: SessionSet = SessionSet.newInstance(session.realm) + set.startDate = session.startDate!! + set.endDate = session.endDate!! + set.breakDuration = session.breakDuration + 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 endedSessions 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 endedSessions + sessions.forEach { session -> + session.sessionSet = set + set.breakDuration = max(set.breakDuration, session.breakDuration) + } +// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") + + } + + /** + * Removes the [session] from the timeline + */ + 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/adapter/RowRepresentableDataSource.kt b/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt index 19e6c255..4f73b66f 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt @@ -68,6 +68,8 @@ interface StaticRowRepresentableDataSource: RowRepresentableDataSource { } } + + /** * Interface extending [RowRepresentableDataSource] to guide the implementation of a live list of [RowRepresentable] * To do that, this interface overrides and provides a default implementation to specific methods of [RowRepresentableDataSource] @@ -89,6 +91,10 @@ class DisplayDescriptor( var context: Context? = null) { } +class UnmanagedRowRepresentableException(message: String) : Exception(message) { + +} + /** * An interface used to provide RowRepresentableAdapter content and value in the form of rows * @@ -100,7 +106,6 @@ class DisplayDescriptor( */ interface DisplayableDataSource { - fun contentDescriptorForRow(row: RowRepresentable): DisplayDescriptor? { return null } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt index ac06e065..c7aaee2b 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt @@ -11,7 +11,9 @@ import io.realm.RealmObject import kotlinx.android.synthetic.main.fragment_editable_data.* import kotlinx.android.synthetic.main.fragment_editable_data.view.* import net.pokeranalytics.android.R +import net.pokeranalytics.android.exceptions.TypeException import net.pokeranalytics.android.model.LiveData +import net.pokeranalytics.android.model.TournamentKind import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Savable import net.pokeranalytics.android.model.realm.Bankroll @@ -47,7 +49,6 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - initData() initUI() } @@ -69,18 +70,17 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { when (row) { - LocationRow.LOCATE_ME -> PlacePickerManager.create(parentActivity, row, this) - else -> { - BottomSheetFragment.create( - fragmentManager, - row, - this, - (this.item as RowRepresentableDataSource).editDescriptors(row) - ) + LocationRow.LOCATE_ME -> { + if (item is Location) { + (item as Location).isLookingForPlaces = true + PlacePickerManager.create(parentActivity, row, this) + rowRepresentableAdapter.refreshRow(row) + } else { + throw TypeException("Need to manage LocationRow.LOCATE_ME for ${item::class.java}") + } } + else -> BottomSheetFragment.create(fragmentManager, row, this, (this.item as RowRepresentableDataSource).editDescriptors(row)) } - - } override fun clickOnAdd(row: RowRepresentable) { @@ -91,14 +91,12 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, this.getRealm().executeTransaction { (this.item as Savable).updateValue(value, row) } - when(row) { - LocationRow.LOCATE_ME -> rowRepresentableAdapter.notifyDataSetChanged() + when (row) { + LocationRow.LOCATE_ME -> { + rowRepresentableAdapter.notifyDataSetChanged() + } else -> rowRepresentableAdapter.refreshRow(row) } - - } - - private fun initData() { } /** @@ -142,12 +140,12 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, } } else { - val message = when(item) { + val message = when (item) { is Bankroll -> R.string.empty_name_for_br_error is Location -> R.string.location_empty_field_error is Game -> R.string.location_empty_field_error is TournamentName -> R.string.tt_empty_field_error - //is TransactionType -> R.string.operation_type_empty_field_error + is TournamentKind -> R.string.tt_empty_field_error else -> throw IllegalStateException("Need to manage ${item::class.java} error") } @@ -157,41 +155,24 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, builder.show() } - } /** * Delete data */ private fun deleteData() { - val builder = AlertDialog.Builder(requireContext()) builder.setTitle(R.string.warning) .setMessage(R.string.are_you_sure_you_want_to_do_that_) .setNegativeButton(R.string.no, null) .setPositiveButton(R.string.yes) { _, _ -> - //TODO: Maybe update this code, does the object need to be managed? this.getRealm().executeTransaction { this.liveDataType.deleteData(it, (this.item as Savable)) } - - /* - if (this.item.isManaged) { - Toast.makeText(requireContext(), "isManaged", Toast.LENGTH_SHORT).show() - Timber.d("is managed") - this.getRealm().executeTransaction { - this.liveDataType.deleteData(it, (this.item as Savable)) - } - } else { - Toast.makeText(requireContext(), "isNotManaged", Toast.LENGTH_SHORT).show() - Timber.d("is not managed") - } - */ this.activity?.finish() } builder.show() - } /** @@ -224,5 +205,13 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, this ) this.recyclerView.adapter = rowRepresentableAdapter + + // When creating an object, open automatically the keyboard for the first row + if (!isUpdating && this.item is RowRepresentableDataSource) { + val row = (this.item as RowRepresentableDataSource).adapterRows()?.firstOrNull() + row?.let { + onRowSelected(0, it) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt index a307e593..3397fcb3 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt @@ -67,7 +67,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource super.onResume() //rows.clear() - //sessions.addAll(getRealm().copyFromRealm(realmSessions)) + //endedSessions.addAll(getRealm().copyFromRealm(realmSessions)) createSessionsHeaders() } @@ -105,7 +105,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource } /** - * Create the sessions headers + * Create the endedSessions headers */ private fun createSessionsHeaders() { 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..906cb013 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,14 @@ 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(), + currentSession.startDate ) SessionRow.BANKROLL -> { BottomSheetFragment.create(fragmentManager, row, this, data, false) @@ -111,7 +112,8 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott sessionAdapter.refreshRow(row) when (row) { SessionRow.CASHED_OUT, SessionRow.PRIZE, SessionRow.NET_RESULT, SessionRow.BUY_IN, SessionRow.TIPS, - SessionRow.START_DATE, SessionRow.END_DATE -> updateSessionUI() + SessionRow.START_DATE, SessionRow.END_DATE, SessionRow.BANKROLL -> updateSessionUI() + SessionRow.BREAK_TIME -> this.sessionAdapter.notifyDataSetChanged() } } @@ -182,8 +184,6 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott floatingActionButton.animate().scaleX(0f).scaleY(0f).alpha(0f) .setInterpolator(FastOutSlowInInterpolator()).start() } - else -> { - } } updateMenuUI() 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 00bdd578..684a1172 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 @@ -21,7 +21,7 @@ import net.pokeranalytics.android.util.NULL_TEXT class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSource { - private var rowRepresentables: ArrayList = ArrayList() + private var rowRepresentables: ArrayList = ArrayList() private lateinit var statsAdapter: RowRepresentableAdapter @@ -56,7 +56,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc } override fun contentDescriptorForRow(row: RowRepresentable): DisplayDescriptor? { - var dc = DisplayDescriptor() + val dc = DisplayDescriptor() dc.textFormat = TextFormat(NULL_TEXT) if (row is StatRepresentable) { context?.let { context -> @@ -107,7 +107,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc val cgSessions = mutableListOf() val tSessions = mutableListOf() - super.sessions.forEach { session -> + super.endedSessions.forEach { session -> if (session.isCashGame()) { cgSessions.add(session) } else { @@ -115,8 +115,8 @@ 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 allSessionGroup = SessionGroup(getString(R.string.all), super.sessions, allStats) + 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.endedSessions, 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) val tStats: List = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN) 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..f974d937 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 @@ -6,12 +6,12 @@ import net.pokeranalytics.android.model.realm.Session open class SessionObserverFragment : PokerAnalyticsFragment() { - val sessions: RealmResults + val endedSessions: RealmResults init { val realm = Realm.getDefaultInstance() - this.sessions = realm.where(Session::class.java).isNotNull("timeFrame.endDate").findAll() - this.sessions.addChangeListener { _, _ -> + this.endedSessions = realm.where(Session::class.java).isNotNull("endDate").findAll() + this.endedSessions.addChangeListener { _, _ -> this.sessionsChanged() } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/helpers/DateTimePickerManager.kt b/app/src/main/java/net/pokeranalytics/android/ui/helpers/DateTimePickerManager.kt index 2cd5b074..f4935267 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/helpers/DateTimePickerManager.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/helpers/DateTimePickerManager.kt @@ -7,6 +7,7 @@ import android.content.DialogInterface import android.text.format.DateFormat import android.widget.DatePicker import android.widget.TimePicker +import android.widget.Toast import net.pokeranalytics.android.R import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.view.RowRepresentable @@ -17,16 +18,18 @@ class DateTimePickerManager : DatePickerDialog.OnDateSetListener, private var context: Context? = null - lateinit var row: RowRepresentable - lateinit var delegate: RowRepresentableDelegate - lateinit var calendar: Calendar + private lateinit var row: RowRepresentable + private lateinit var delegate: RowRepresentableDelegate + private lateinit var calendar: Calendar + private var minimumDate: Date? = null companion object { fun create( context: Context, row: RowRepresentable, delegate: RowRepresentableDelegate, - date: Date? + date: Date?, + minimumDate: Date? = null ) : DateTimePickerManager { val calendar = Calendar.getInstance() @@ -37,6 +40,7 @@ class DateTimePickerManager : DatePickerDialog.OnDateSetListener, dateTimePickerManager.row = row dateTimePickerManager.delegate = delegate dateTimePickerManager.calendar = calendar + dateTimePickerManager.minimumDate = minimumDate dateTimePickerManager.showDatePicker() @@ -54,7 +58,19 @@ class DateTimePickerManager : DatePickerDialog.OnDateSetListener, override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) { calendar.set(Calendar.HOUR_OF_DAY, hourOfDay) calendar.set(Calendar.MINUTE, minute) - delegate.onRowValueChanged(calendar.time, row) + + if (minimumDate != null) { + minimumDate?.let { + if (calendar.time < it) { + Toast.makeText(context, R.string.end_date_not_possible, Toast.LENGTH_LONG).show() + } else { + delegate.onRowValueChanged(calendar.time, row) + } + } + } else { + delegate.onRowValueChanged(calendar.time, row) + } + } /** @@ -66,6 +82,10 @@ class DateTimePickerManager : DatePickerDialog.OnDateSetListener, val day = calendar.get(Calendar.DAY_OF_MONTH) context?.let { val datePickerDialog = DatePickerDialog(it, this, year, month, day) + minimumDate?.let { + datePickerDialog.datePicker.minDate = it.time + } + datePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, it.getString(R.string.clear)) { dialog, _ -> delegate.onRowValueChanged(null, row) dialog.dismiss() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/helpers/PlacePickerManager.kt b/app/src/main/java/net/pokeranalytics/android/ui/helpers/PlacePickerManager.kt index cb4adbcc..919653c2 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/helpers/PlacePickerManager.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/helpers/PlacePickerManager.kt @@ -16,22 +16,26 @@ class PlacePickerManager { activity: PokerAnalyticsActivity, row: RowRepresentable, delegate: RowRepresentableDelegate, - maxResults: Int? = 2 + maxResults: Int? = 3 ) { activity.askForPlacesRequest { success, places -> if (success && places.size > 0) { val placesArray = ArrayList() for ((index, place) in places.withIndex()) { placesArray.add(place.place.name.toString()) - if (index == maxResults) { + if (index == (maxResults ?: 3) - 1) { break } } val builder = AlertDialog.Builder(activity) builder.setItems(placesArray.toTypedArray()) { _, which -> delegate.onRowValueChanged(places[which].place, row) + }.setOnCancelListener { + delegate.onRowValueChanged(null, row) } builder.show() + } else { + delegate.onRowValueChanged(null, row) } } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index 1ed2042a..086b172e 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -7,6 +7,8 @@ import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.SwitchCompat import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import androidx.core.widget.ContentLoadingProgressBar import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.row_history_session.view.* import kotlinx.android.synthetic.main.row_stats_title_value.view.* @@ -52,7 +54,7 @@ enum class RowViewType(private var layoutRes: Int) { STAT(R.layout.row_stats_title_value), // Separator - SEPARATOR(R.layout.row_separator), ; + SEPARATOR(R.layout.row_separator); /** * View holder @@ -205,6 +207,8 @@ enum class RowViewType(private var layoutRes: Int) { inner class RowButtonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { itemView.findViewById(R.id.title).text = row.localizedTitle(itemView.context) + itemView.findViewById(R.id.title).isVisible = !adapter.dataSource.boolForRow(row) + itemView.findViewById(R.id.progressBar).isVisible = adapter.dataSource.boolForRow(row) val listener = View.OnClickListener { adapter.delegate?.onRowSelected(position, row) } 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..320f66ee 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,12 @@ 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.getFormattedDuration() +// } + rowHistorySession.sessionInfoDurationValue.text = session.getFormattedDuration() // Location rowHistorySession.sessionInfoLocationIcon.isVisible = session.location != null @@ -115,19 +116,28 @@ class SessionRowView : FrameLayout { rowHistorySession.sessionInfoTableValue.text = TableSize(it).localizedTitle(context) } + val state = session.getState() + rowHistorySession.sessionInfoDurationIcon.isVisible = state.hasStarted + rowHistorySession.sessionInfoDurationValue.isVisible = state.hasStarted + // State - if (session.getState() == SessionState.STARTED) { + if (state == SessionState.STARTED) { rowHistorySession.gameResult.isVisible = false rowHistorySession.infoIcon.isVisible = true rowHistorySession.infoIcon.setImageResource(R.drawable.chip) rowHistorySession.infoTitle.isVisible = true rowHistorySession.infoTitle.text = context.getString(R.string.running_session_state) - } else if (session.getState() == SessionState.PLANNED) { + } else if (state == SessionState.PLANNED) { rowHistorySession.gameResult.isVisible = false 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 if (state == SessionState.PENDING) { + rowHistorySession.gameResult.isVisible = false + rowHistorySession.infoIcon.isVisible = false + rowHistorySession.infoTitle.isVisible = false + } else { rowHistorySession.gameResult.isVisible = true rowHistorySession.infoIcon.isVisible = false diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt index 68eb181d..851e6375 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt @@ -12,7 +12,7 @@ enum class BankrollRow : RowRepresentable { override val resId: Int? get() { return when (this) { - LIVE -> R.string.live + LIVE -> R.string.online } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SessionRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SessionRow.kt index c17342b9..e5ac8960 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SessionRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SessionRow.kt @@ -75,7 +75,7 @@ enum class SessionRow : RowRepresentable { ) } else { arrayListOf( - NET_RESULT, BUY_IN, TIPS, + NET_RESULT, SeparatorRowRepresentable(), GAME, BLINDS, LOCATION, BANKROLL, TABLE_SIZE, START_DATE, END_DATE, BREAK_TIME, COMMENT ) diff --git a/app/src/main/java/net/pokeranalytics/android/util/DateExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/DateExtension.kt index 6029e96b..36c0a677 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/DateExtension.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/DateExtension.kt @@ -85,7 +85,7 @@ fun Date.getMonthAndYear(): String { } // Return the netDuration between two dates -fun Date.getDuration(toDate: Date) : String { +fun Date.getFormattedDuration(toDate: Date) : String { val difference = (toDate.time - this.time).toInt() val numOfDays = (difference / (1000 * 60 * 60 * 24)) val hours = (difference / (1000 * 60 * 60)) diff --git a/app/src/main/res/layout/row_button.xml b/app/src/main/res/layout/row_button.xml index 88759911..3a06d293 100644 --- a/app/src/main/res/layout/row_button.xml +++ b/app/src/main/res/layout/row_button.xml @@ -4,23 +4,36 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="56dp" android:background="?selectableItemBackground"> + + Hourly Rate - Number of groups + Number of sessions Number of games Average duration Net(BB) per 100 hands @@ -20,6 +20,7 @@ Address %s deleted + The end date should be after the start date