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