Compare commits

...

6 Commits

  1. 4
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt
  2. 47
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  3. 318
      app/src/main/java/net/pokeranalytics/android/calculus/DataManager.kt
  4. 189
      app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt
  6. 10
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReportManager.kt
  7. 11
      app/src/main/java/net/pokeranalytics/android/calculus/optimalduration/CashGameOptimalDurationCalculator.kt
  8. 2
      app/src/main/java/net/pokeranalytics/android/model/Criteria.kt
  9. 19
      app/src/main/java/net/pokeranalytics/android/model/LiveData.kt
  10. 7
      app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt
  11. 1
      app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt
  12. 4
      app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt
  13. 66
      app/src/main/java/net/pokeranalytics/android/model/migrations/Patcher.kt
  14. 42
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  15. 10
      app/src/main/java/net/pokeranalytics/android/model/realm/ComputableResult.kt
  16. 18
      app/src/main/java/net/pokeranalytics/android/model/realm/Currency.kt
  17. 38
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  18. 15
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomFieldEntry.kt
  19. 17
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  20. 9
      app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt
  21. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  22. 26
      app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt
  23. 481
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  24. 18
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  25. 49
      app/src/main/java/net/pokeranalytics/android/model/realm/UserConfig.kt
  26. 20
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  27. 2
      app/src/main/java/net/pokeranalytics/android/model/utils/DataUtils.kt
  28. 19
      app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt
  29. 209
      app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt
  30. 28
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  31. 39
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/MediaActivity.kt
  32. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt
  33. 11
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  34. 11
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  35. 16
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt
  36. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/Top10Fragment.kt
  37. 15
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  38. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/FilterableFragment.kt
  39. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt
  40. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt
  41. 49
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/AbstractReportFragment.kt
  42. 11
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  43. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  44. 6
      app/src/main/java/net/pokeranalytics/android/ui/modules/bankroll/BankrollFragment.kt
  45. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt
  46. 16
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt
  47. 5
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/GridCalendarViewModel.kt
  48. 27
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/DataManagerFragment.kt
  49. 20
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/EditableDataFragment.kt
  50. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/PlayerDataFragment.kt
  51. 9
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/PlayerDataViewModel.kt
  52. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/TransactionDataFragment.kt
  53. 12
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt
  54. 6
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedSessionRowRepresentableAdapter.kt
  55. 5
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/NewDataMenuActivity.kt
  56. 10
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt
  57. 13
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterHandler.kt
  58. 23
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt
  59. 22
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersListFragment.kt
  60. 11
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/editor/EditorFragment.kt
  61. 96
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/EditorViewModel.kt
  62. 22
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt
  63. 111
      app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt
  64. 16
      app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionViewModel.kt
  65. 9
      app/src/main/java/net/pokeranalytics/android/ui/modules/settings/DealtHandsPerHourFragment.kt
  66. 12
      app/src/main/java/net/pokeranalytics/android/ui/modules/settings/TransactionFilterFragment.kt
  67. 3
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  68. 6
      app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt
  69. 10
      app/src/main/java/net/pokeranalytics/android/ui/view/rows/SessionPropertiesRow.kt
  70. 34
      app/src/main/java/net/pokeranalytics/android/ui/viewmodel/BottomSheetViewModel.kt
  71. 2
      app/src/main/java/net/pokeranalytics/android/util/BackupOperator.kt
  72. 24
      app/src/main/java/net/pokeranalytics/android/util/FakeDataManager.kt
  73. 5
      app/src/main/java/net/pokeranalytics/android/util/csv/CSVDescriptor.kt
  74. 23
      app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt
  75. 10
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
  76. 47
      app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt
  77. 2
      app/src/main/res/layout/activity_new_data.xml

@ -186,13 +186,13 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
Assert.fail("No std100 stat")
}
results.computedStat(Stat.MAXIMUM_NETRESULT)?.let {
results.computedStat(Stat.MAXIMUM_NET_RESULT)?.let {
assertEquals(300.0, it.value, delta)
} ?: run {
Assert.fail("No MAXIMUM_NETRESULT")
}
results.computedStat(Stat.MINIMUM_NETRESULT)?.let {
results.computedStat(Stat.MINIMUM_NET_RESULT)?.let {
assertEquals(-100.0, it.value, delta)
} ?: run {
Assert.fail("No MINIMUM_NETRESULT")

@ -6,14 +6,13 @@ import android.os.Build
import com.google.firebase.FirebaseApp
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.kotlin.where
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.ReportWhistleBlower
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.model.migrations.Patcher
import net.pokeranalytics.android.model.migrations.PokerAnalyticsMigration
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.UserConfigObserver
import net.pokeranalytics.android.model.utils.Seed
import net.pokeranalytics.android.util.*
import net.pokeranalytics.android.util.billing.AppGuard
@ -23,7 +22,6 @@ import java.util.*
class PokerAnalyticsApplication : Application() {
var reportWhistleBlower: ReportWhistleBlower? = null
var backupOperator: BackupOperator? = null
companion object {
@ -44,6 +42,11 @@ class PokerAnalyticsApplication : Application() {
UserDefaults.init(this)
if (BuildConfig.DEBUG) {
// Logs
Timber.plant(PokerAnalyticsLogs())
}
// AppGuard / Billing services
AppGuard.load(this.applicationContext)
@ -51,8 +54,8 @@ class PokerAnalyticsApplication : Application() {
Realm.init(this)
val realmConfiguration = RealmConfiguration.Builder()
.name(Realm.DEFAULT_REALM_NAME)
.schemaVersion(14)
.allowWritesOnUiThread(true)
.schemaVersion(15)
// .allowWritesOnUiThread(true)
.migration(PokerAnalyticsMigration())
.initialData(Seed(this))
.build()
@ -63,24 +66,20 @@ class PokerAnalyticsApplication : Application() {
CrashLogging.log("App onCreate. Locales = $locales")
}
if (BuildConfig.DEBUG) {
// Logs
Timber.plant(PokerAnalyticsLogs())
}
Timber.d("SDK version = ${Build.VERSION.SDK_INT}")
if (BuildConfig.DEBUG) {
Timber.d("UserPreferences.defaultCurrency: ${UserDefaults.currency.symbol}")
Timber.d("Realm path = ${Realm.getDefaultInstance().path}")
// this.createFakeSessions()
}
// Patch
Patcher.patchAll(this)
// Report
this.reportWhistleBlower = ReportWhistleBlower(this.applicationContext)
// Processors
DataManager.configure(this.applicationContext)
UserConfigObserver.create()
// this.reportWhistleBlower = ReportWhistleBlower(this.applicationContext)
// Backups
this.backupOperator = BackupOperator(this.applicationContext)
@ -89,11 +88,17 @@ class PokerAnalyticsApplication : Application() {
val locale = Locale.getDefault()
CrashLogging.log("Country: ${locale.country}, language: ${locale.language}")
// val realm = Realm.getDefaultInstance()
// val v:Int = 0
// val set = realm.where<SessionSet>().equalTo("sessions.type", v).findAll()
// Timber.d("SESSION SET COUNT = ${set.size}")
// Realm.getDefaultInstance().executeTransaction {
// it.delete(Performance::class.java)
// }
// createFakeSessions()
}
/**
@ -101,15 +106,15 @@ class PokerAnalyticsApplication : Application() {
*/
private fun createFakeSessions() {
val realm = Realm.getDefaultInstance()
val sessionsCount = realm.where<Session>().count()
realm.close()
// val realm = Realm.getDefaultInstance()
// val sessionsCount = realm.where<Session>().count()
// realm.close()
if (sessionsCount < 10) {
CoroutineScope(context = Dispatchers.IO).launch {
FakeDataManager.createFakeSessions(500)
// if (sessionsCount < 10) {
CoroutineScope(context = Dispatchers.Default).launch {
FakeDataManager.createFakeSessions(8000)
}
}
// }
}

@ -0,0 +1,318 @@
package net.pokeranalytics.android.calculus
import android.content.Context
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Currency
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import kotlin.math.max
class CorruptSessionSetException(message: String) : Exception(message)
/**
* The manager is in charge of updating the abstract concept of timeline,
* representing the sequenced time frames where the user plays.
*/
object DataManager {
private var sessions: RealmResults<Session>? = null
private var currencies: RealmResults<Currency>? = null
private var dateModifiedSessionIds: MutableSet<String> = mutableSetOf()
private var statsToComputeSessionIds: MutableSet<String> = mutableSetOf()
private var changedCurrencies: MutableSet<String> = mutableSetOf()
var reportWhistleBlower: ReportWhistleBlower? = null
init {
val realm = Realm.getDefaultInstance()
sessions = realm.where(Session::class.java).findAllAsync()
sessions?.addChangeListener { sessions ->
// if (this.dateModifiedSessionIds.isNotEmpty() || this.statsToComputeSessionIds.isNotEmpty()) {
sessions.realm.writeAsync { asyncRealm ->
computeStatsIfNecessary(asyncRealm)
computeDatesIfNecessary(asyncRealm)
reportWhistleBlower?.requestReportLaunch()
}
// }
}
this.currencies = realm.where(Currency::class.java).findAll()
this.currencies?.addChangeListener { currencies, _ ->
if (changedCurrencies.isNotEmpty()) {
currencies.realm.writeAsync { asyncRealm ->
for (currencyId in this.changedCurrencies) {
asyncRealm.findById<Currency>(currencyId)?.let { currency ->
Timber.d("Compute currency ${currency.code} ")
currency.refreshRelatedRatedValues()
}
}
changedCurrencies.clear()
}
}
}
realm.close()
}
fun configure(context: Context) {
reportWhistleBlower = ReportWhistleBlower(context)
}
fun sessionToCompute(session: Session) {
// Timber.d("sessionToCompute, date = ${session.startDate} / ${session.endDate}")
statsToComputeSessionIds.add(session.id)
}
fun sessionDateChanged(session: Session) {
// Timber.d("sessionDateChanged")
dateModifiedSessionIds.add(session.id)
}
fun currencyToCompute(currency: Currency) {
// Timber.d("currencyToCompute")
changedCurrencies.add(currency.id)
}
private fun computeStatsIfNecessary(realm: Realm) {
if (statsToComputeSessionIds.isNotEmpty()) {
for (sessionId in statsToComputeSessionIds) {
realm.findById<Session>(sessionId)?.let { session ->
// Timber.d("Session Manager > compute stats, set = ${session.sessionSet}")
session.computeStats()
session.sessionSet?.computeStats()
}
}
statsToComputeSessionIds.clear()
}
}
private fun computeDatesIfNecessary(realm: Realm) {
if (dateModifiedSessionIds.isNotEmpty()) {
for (sessionId in dateModifiedSessionIds) {
realm.findById<Session>(sessionId)?.let { session ->
// Timber.d("Session Manager > manage dates, set = ${session.sessionSet}")
if (session.endDate != null) {
updateTimeline(session)
} else if (session.sessionSet != null) {
removeFromTimeline(session)
}
}
}
dateModifiedSessionIds.clear()
}
}
/**
* Updates the global timeline using the updated [session]
*/
fun updateTimeline(session: Session) {
// Timber.d("updateTimeline...")
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
if (session.startDate == null) {
throw ModelException("Start date should never be null here")
}
if (session.endDate == null) {
throw ModelException("End date should never be null here")
}
val sessionSets = matchingSets(session)
cleanupSessionSets(session, sessionSets)
}
private fun matchingSets(session: Session): RealmResults<SessionSet> {
val realm = session.realm
val endDate = session.endDate!! // tested above
val startDate = session.startDate!!
val query: RealmQuery<SessionSet> = 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)
return query.findAll()
}
/**
* Multiple session sets update:
* Merges or splits session sets
* Does that by deleting then recreating
*/
private fun cleanupSessionSets(session: Session, sessionSets: RealmResults<SessionSet>) {
// Timber.d("cleanupSessionSets...")
// get all endedSessions from sets
val allImpactedSessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { allImpactedSessions.addAll(it) }
}
allImpactedSessions.add(session)
// delete all sets
sessionSets.deleteAllFromRealm()
allImpactedSessions.forEach { impactedSession ->
val sets = matchingSets(impactedSession)
updateTimeFrames(sets, impactedSession)
}
}
/**
* Update the global timeline using the impacted [sessionSets] and the updated [session]
*/
private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, session: Session) {
// Timber.d("updateTimeFrames...")
when (sessionSets.size) {
0 -> createOrUpdateSessionSet(session)
else -> mergeSessionGroups(session, sessionSets)
}
}
/**
* Creates or update the session set for the [session]
*/
private fun createOrUpdateSessionSet(session: Session) {
// Timber.d("createOrUpdateSessionSet...")
val set = session.sessionSet
if (set != null) {
set.startDate = session.startDate!! // tested above
set.endDate = session.endDate!!
} else {
createSessionSet(session)
}
}
/**
* Create a set and affect it to the [session]
*/
private fun createSessionSet(session: Session) {
// Timber.d("createSessionSet...")
val realm = session.realm
val set = SessionSet.newInstance(realm)
set.startDate = session.startDate!!
set.endDate = session.endDate!!
set.breakDuration = session.breakDuration
session.sessionSet = set
set.computeStats()
// Timber.d("SET SESSION count = ${set.sessions?.size}")
//
// val t = 0
// val f = realm.where<SessionSet>().equalTo("sessions.type", t).findAll()
// Timber.d("CASH SET COUNT = ${f.size}")
}
/**
* Multiple session sets update:
* Merges all sets into one (delete all then create a new one)
*/
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) {
// Timber.d("mergeSessionGroups")
var startDate = session.startDate!!
var endDate = session.endDate!!
// get all endedSessions from sets
val sessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { sessions.addAll(it) }
}
// find earlier and later dates from all sets
sessions.forEach { s ->
if (s.startDate != null && s.endDate != null) {
val start = s.startDate!!
val end = s.endDate!!
if (start.before(startDate)) {
startDate = start
}
if (end.after(endDate)) {
endDate = end
}
} else {
throw CorruptSessionSetException("Set contains unfinished sessions!")
}
}
// delete all sets
sessionSets.deleteAllFromRealm()
// Create a new set
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = startDate
set.endDate = endDate
// Add the session linked to this timeframe to the new sessionGroup
session.sessionSet = set
// Add all orphan endedSessions
sessions.forEach { s ->
s.sessionSet = set
set.breakDuration = max(set.breakDuration, s.breakDuration)
}
set.computeStats()
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Removes the [session] from the timeline
*/
fun removeFromTimeline(session: Session) {
// Timber.d("removeFromTimeline")
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
val sessionSet = session.sessionSet
if (sessionSet != null) {
val sessions = mutableSetOf<Session>()
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) }
sessions.remove(session)
// Timber.d(">>> sessionSet.deleteFromRealm")
sessionSet.deleteFromRealm()
sessions.forEach {
updateTimeline(it)
}
}
}
}

@ -1,20 +1,17 @@
package net.pokeranalytics.android.calculus
import android.content.Context
import android.os.CountDownTimer
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.optimalduration.CashGameOptimalDurationCalculator
import net.pokeranalytics.android.model.LiveOnline
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.Performance
import net.pokeranalytics.android.model.realm.PerformanceKey
import net.pokeranalytics.android.ui.view.rows.StaticReport
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.formattedHourlyDuration
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
import net.pokeranalytics.android.util.extensions.writeAsync
interface NewPerformanceListener {
@ -23,8 +20,8 @@ interface NewPerformanceListener {
class ReportWhistleBlower(var context: Context) {
private var sessions: RealmResults<Session>? = null
private var results: RealmResults<Result>? = null
// private var sessions: RealmResults<Session>? = null
// private var results: RealmResults<Result>? = null
private var currentTask: ReportTask? = null
@ -34,23 +31,23 @@ class ReportWhistleBlower(var context: Context) {
private var paused: Boolean = false
private var timer: CountDownTimer? = null
// private var timer: CountDownTimer? = null
init {
val realm = Realm.getDefaultInstance()
this.sessions = realm.where(Session::class.java).findAll()
this.sessions?.addChangeListener { _ ->
requestReportLaunch()
}
this.results = realm.where(Result::class.java).findAll()
this.results?.addChangeListener { _ ->
requestReportLaunch()
}
realm.close()
// val realm = Realm.getDefaultInstance()
//
// this.sessions = realm.where(Session::class.java).findAll()
// this.sessions?.addChangeListener { _ ->
// requestReportLaunch()
// }
//
//// this.results = realm.where(Result::class.java).findAll()
//// this.results?.addChangeListener { _ ->
//// requestReportLaunch()
//// }
//
// realm.close()
}
fun addListener(newPerformanceListener: NewPerformanceListener) {
@ -62,24 +59,26 @@ class ReportWhistleBlower(var context: Context) {
}
fun requestReportLaunch() {
Timber.d(">>> Launch report")
// Timber.d(">>> Launch report")
if (paused) {
return
}
this.timer?.cancel()
val launchStart = 100L
val timer = object: CountDownTimer(launchStart, launchStart) {
override fun onTick(p0: Long) { }
override fun onFinish() {
launchReportTask()
}
}
this.timer = timer
timer.start()
launchReportTask()
// this.timer?.cancel()
//
// val launchStart = 100L
// val timer = object : CountDownTimer(launchStart, launchStart) {
// override fun onTick(p0: Long) {}
//
// override fun onFinish() {
// launchReportTask()
// }
// }
// this.timer = timer
// timer.start()
}
@ -106,7 +105,11 @@ class ReportWhistleBlower(var context: Context) {
fun resume() {
this.paused = false
this.requestReportLaunch()
val realm = Realm.getDefaultInstance()
realm.writeAsync {
this.requestReportLaunch()
}
realm.close()
}
fun has(performanceId: String): Boolean {
@ -131,8 +134,8 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
private var cancelled = false
private val coroutineContext: CoroutineContext
get() = Dispatchers.Default
// private val coroutineContext: CoroutineContext
// get() = Dispatchers.Default
fun start() {
launchReports()
@ -143,35 +146,35 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
private fun launchReports() {
CoroutineScope(coroutineContext).launch {
// CoroutineScope(coroutineContext).launch {
val realm = Realm.getDefaultInstance()
val realm = Realm.getDefaultInstance()
// Basic
for (basicReport in StaticReport.basicReports) {
if (cancelled) {
break
}
launchReport(realm, basicReport)
// Basic
for (basicReport in StaticReport.basicReports) {
if (cancelled) {
break
}
launchReport(realm, basicReport)
}
// CustomField
val customFields = realm.where(CustomField::class.java)
.equalTo("type", CustomField.Type.LIST.uniqueIdentifier).findAll()
for (customField in customFields) {
if (cancelled) {
break
}
launchReport(realm, StaticReport.CustomFieldList(customField))
// CustomField
val customFields = realm.where(CustomField::class.java)
.equalTo("type", CustomField.Type.LIST.uniqueIdentifier).findAll()
for (customField in customFields) {
if (cancelled) {
break
}
realm.close()
launchReport(realm, StaticReport.CustomFieldList(customField))
}
realm.close()
// }
}
private fun launchReport(realm: Realm, report: StaticReport) {
Timber.d(">>> launch report = $report")
// Timber.d(">>> launch report = $report")
when (report) {
StaticReport.OptimalDuration -> launchOptimalDuration(realm, report)
@ -202,7 +205,7 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
for (stat in result.options.stats) {
Timber.d("analyse stat: $stat for report: $staticReport")
// Timber.d("analyse stat: $stat for report: $staticReport")
// Get current performance
var query = performancesQuery(realm, staticReport, stat)
@ -212,7 +215,10 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
customField?.let {
query = query.equalTo("customFieldId", it.id)
}
val currentPerf = query.findFirst()
var currentPerf = query.findFirst()
if (currentPerf != null) {
currentPerf = realm.copyFromRealm(currentPerf)
}
// Store if necessary, delete if necessary
val bestComputedResults = result.max(stat)
@ -221,12 +227,16 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
val performanceQuery = computedResults.group.query
val performanceName = performanceQuery.getName(this.context, nameSeparator)
Timber.d("Best computed = $performanceName, ${computedResults.computedStat(Stat.NET_RESULT)?.value}")
// Timber.d("Best computed = $performanceName, ${computedResults.computedStat(Stat.NET_RESULT)?.value}")
var storePerf = true
currentPerf?.let {
currentPerf.name?.let { name ->
if (computedResults.group.query.getName(this.context, nameSeparator) == name) {
if (computedResults.group.query.getName(
this.context,
nameSeparator
) == name
) {
storePerf = false
}
}
@ -237,11 +247,10 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
if (storePerf) {
realm.executeTransaction {
currentPerf.name = performanceName
currentPerf.objectId = performanceQuery.objectId
currentPerf.customFieldId = customField?.id
}
currentPerf.name = performanceName
currentPerf.objectId = performanceQuery.objectId
currentPerf.customFieldId = customField?.id
// realm.copyToRealm(currentPerf)
this.whistleBlower.notify(currentPerf)
}
@ -256,17 +265,17 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
customField?.id,
null
)
realm.executeTransaction { it.copyToRealm(performance) }
realm.copyToRealm(performance)
this.whistleBlower.notify(performance)
}
} ?: run { // if there is no max but a now irrelevant Performance, we delete it
Timber.d("NO best computed value, current perf = $currentPerf ")
// Timber.d("NO best computed value, current perf = $currentPerf ")
currentPerf?.let { perf ->
realm.executeTransaction {
Timber.d("Delete perf: stat = ${perf.stat}, report = ${perf.reportId}")
perf.deleteFromRealm()
}
// Timber.d("Delete perf: stat = ${perf.stat}, report = ${perf.reportId}")
realm.findById<Performance>(perf.id)?.deleteFromRealm()
// perf.deleteFromRealm()
}
}
@ -274,9 +283,17 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
private fun analyseOptimalDuration(realm: Realm, staticReport: StaticReport, key: PerformanceKey, duration: Double?) {
private fun analyseOptimalDuration(
realm: Realm,
staticReport: StaticReport,
key: PerformanceKey,
duration: Double?
) {
val performance = performancesQuery(realm, staticReport, key).findFirst()
var performance = performancesQuery(realm, staticReport, key).findFirst()
if (performance != null) {
performance = realm.copyFromRealm(performance)
}
duration?.let {
var storePerf = true
@ -288,30 +305,32 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
if (storePerf) {
realm.executeTransaction {
perf.name = formattedDuration
perf.value = duration
}
perf.name = formattedDuration
perf.value = duration
// realm.copyToRealm(perf)
}
}
if (storePerf) {
val perf = Performance(staticReport, key, name = formattedDuration, value = duration)
realm.executeTransaction { it.copyToRealm(perf) }
val perf =
Performance(staticReport, key, name = formattedDuration, value = duration)
realm.copyToRealm(perf)
this.whistleBlower.notify(perf)
}
} ?: run { // no duration
performance?.let { perf ->
realm.executeTransaction {
perf.deleteFromRealm() // delete if the perf exists
}
realm.findById<Performance>(perf.id)?.deleteFromRealm()
}
}
}
private fun performancesQuery(realm: Realm, staticReport: StaticReport, key: PerformanceKey): RealmQuery<Performance> {
private fun performancesQuery(
realm: Realm,
staticReport: StaticReport,
key: PerformanceKey
): RealmQuery<Performance> {
return realm.where(Performance::class.java)
.equalTo("reportId", staticReport.uniqueIdentifier)
.equalTo("key", key.value)

@ -89,7 +89,7 @@ class BankrollCalculator {
this.computeRiskOfRuin(report, result)
} else {
val results = Filter.queryOn<Result>(realm, baseQuery)
val results = Filter.queryOn<Session>(realm, baseQuery)
report.netResult = results.sum("net").toDouble()
}

@ -2,8 +2,8 @@ package net.pokeranalytics.android.calculus.bankroll
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.model.realm.Bankroll
@ -68,12 +68,12 @@ object BankrollReportManager {
}
// otherwise compute it
GlobalScope.launch(coroutineContext) {
CoroutineScope(context = coroutineContext).launch {
var report: BankrollReport? = null
val coroutine = GlobalScope.async {
val coroutine = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>>>> start computing bankroll...")
// Timber.d(">>>>> start computing bankroll...")
val realm = Realm.getDefaultInstance()
@ -84,7 +84,7 @@ object BankrollReportManager {
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>>>> ended in $duration seconds")
// Timber.d(">>>>> ended in $duration seconds")
}
coroutine.await()

@ -8,7 +8,6 @@ import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.Session
import org.apache.commons.math3.fitting.PolynomialCurveFitter
import org.apache.commons.math3.fitting.WeightedObservedPoints
import timber.log.Timber
import java.util.*
import kotlin.math.pow
import kotlin.math.round
@ -65,7 +64,7 @@ class CashGameOptimalDurationCalculator {
var validBuckets = 0
val hkeys = sessionsByDuration.keys.map { it / 3600 / 1000.0 }.sorted()
Timber.d("Stop notif > keys: $hkeys ")
// Timber.d("Stop notif > keys: $hkeys ")
for (key in sessionsByDuration.keys.sorted()) {
val sessionCount = sessionsByDuration[key]?.size ?: 0
if (start == null && sessionCount >= minimumValidityCount) {
@ -76,15 +75,15 @@ class CashGameOptimalDurationCalculator {
validBuckets++
}
}
Timber.d("Stop notif > validBuckets: $validBuckets ")
// Timber.d("Stop notif > validBuckets: $validBuckets ")
if (!(start != null && end != null && (end - start) >= intervalValidity)) {
Timber.d("Stop notif > invalid setup: $start / $end ")
// Timber.d("Stop notif > invalid setup: $start / $end ")
return null
}
// define if we have enough sessions
if (sessions.size < 50) {
Timber.d("Stop notif > not enough sessions: ${sessions.size} ")
// Timber.d("Stop notif > not enough sessions: ${sessions.size} ")
return null
}
@ -134,7 +133,7 @@ class CashGameOptimalDurationCalculator {
return bestDuration
}
Timber.d("Stop notif > not found, best duration: $bestDuration")
// Timber.d("Stop notif > not found, best duration: $bestDuration")
realm.close()
return null
}

@ -87,7 +87,7 @@ sealed class Criteria(override var uniqueIdentifier: Int) : IntIdentifiable, Row
if (this is ValueCustomFields) {
val realm = Realm.getDefaultInstance()
val distincts = realm.where<CustomFieldEntry>().equalTo("customFields.id", this.customFieldId).findAll().sort("numericValue", Sort.ASCENDING)
val distincts = realm.where<CustomFieldEntry>().equalTo("customField.id", this.customFieldId).findAll().sort("numericValue", Sort.ASCENDING)
realm.close()
val objects = mutableListOf<QueryCondition.CustomFieldNumberQuery>()

@ -51,12 +51,17 @@ enum class LiveData : Localizable {
}
fun updateOrCreate(realm: Realm, primaryKey: String?): Deletable {
val proxyItem: Deletable? = this.getData(realm, primaryKey)
proxyItem?.let {
return realm.copyFromRealm(it)
} ?: run {
return this.newEntity()
}
val data = this.getData(realm, primaryKey)
data?.let { return data }
return this.newEntity()
// val proxyItem: Deletable? = this.getData(realm, primaryKey)
// proxyItem?.let {
// return realm.copyFromRealm(it)
// } ?: run {
// return this.newEntity()
// }
}
private fun newEntity(): Deletable {
@ -68,7 +73,7 @@ enum class LiveData : Localizable {
primaryKey?.let {
val t = realm.findById(this.relatedEntity, it)
t?.let {
proxyItem = t
proxyItem = realm.copyFromRealm(t) // make an unmanaged object
}
}
return proxyItem

@ -3,7 +3,10 @@ package net.pokeranalytics.android.model.filter
import io.realm.RealmModel
import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.util.CrashLogging
/**
@ -63,7 +66,7 @@ class FilterHelper {
ComputableResult::class.java -> ComputableResult.fieldNameForQueryType(queryCondition)
SessionSet::class.java -> SessionSet.fieldNameForQueryType(queryCondition)
Transaction::class.java -> Transaction.fieldNameForQueryType(queryCondition)
Result::class.java -> Result.fieldNameForQueryType(queryCondition)
// Result::class.java -> Result.fieldNameForQueryType(queryCondition)
else -> {
CrashLogging.logException(PAIllegalStateException("Filterable type fields are not defined for condition ${queryCondition::class}, class ${T::class}"))
null

@ -108,7 +108,6 @@ class Query {
return Query(this)
}
/*
Returns the first object Id of any QueryCondition
*/

@ -96,7 +96,9 @@ sealed class QueryCondition : RowRepresentable {
inline fun <reified T : Filterable, reified S : QueryCondition, reified U : Comparable<U>> distinct(): RealmResults<T>? {
FilterHelper.fieldNameForQueryType<T>(S::class.java)?.let {
val realm = Realm.getDefaultInstance()
realm.refresh()
if (!realm.isInTransaction) {
realm.refresh()
}
val distincts = when (T::class) {
String::class, Int::class -> realm.where<T>().distinct(it).findAll().sort(it, Sort.ASCENDING)

@ -4,14 +4,15 @@ import android.content.Context
import io.realm.Realm
import io.realm.kotlin.where
import net.pokeranalytics.android.PokerAnalyticsApplication
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.model.utils.Seed
import net.pokeranalytics.android.model.utils.SessionSetManager
import net.pokeranalytics.android.util.BLIND_SEPARATOR
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.writeAsync
import java.text.NumberFormat
class Patcher {
@ -61,7 +62,7 @@ class Patcher {
patchRatedAmounts()
}
patchPerformances(application)
patchPerformances()
}
@ -70,8 +71,8 @@ class Patcher {
val transactionTypes = TransactionType.Value.values()
realm.executeTransaction {
Seed.createDefaultTransactionTypes(transactionTypes, context, realm)
realm.writeAsync { asyncRealm ->
Seed.createDefaultTransactionTypes(transactionTypes, context, asyncRealm)
}
realm.close()
@ -80,19 +81,17 @@ class Patcher {
private fun patchBreaks() {
val realm = Realm.getDefaultInstance()
val sets = realm.where(SessionSet::class.java).findAll()
val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.IsCash))
val results = realm.where(Result::class.java).findAll()
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
val sets = asyncRealm.where(SessionSet::class.java).findAll()
val sessions = Filter.queryOn<Session>(asyncRealm, Query(QueryCondition.IsCash))
sets.forEach {
it.computeStats()
}
sessions.forEach {
it.generateStakes()
it.defineHighestBet()
}
results.forEach {
it.computeNumberOfRebuy()
}
}
@ -103,8 +102,8 @@ class Patcher {
private fun patchDefaultTransactionTypes(context: Context) {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val tts = realm.where(TransactionType::class.java).findAll()
realm.writeAsync { asyncRealm ->
val tts = asyncRealm.where(TransactionType::class.java).findAll()
tts.forEach { tt ->
tt.kind?.let { kind ->
val value = TransactionType.Value.values()[kind]
@ -117,8 +116,8 @@ class Patcher {
private fun patchStakes() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val sessions = realm.where(Session::class.java).findAll()
realm.writeAsync { asyncRealm ->
val sessions = asyncRealm.where(Session::class.java).findAll()
sessions.forEach { session ->
val blinds = arrayListOf(session.cgOldSmallBlind, session.cgOldBigBlind).filterNotNull()
val blindsFormatted = blinds.map { NumberFormat.getInstance().format(it) }
@ -128,7 +127,7 @@ class Patcher {
}
}
val handHistories = realm.where(HandHistory::class.java).findAll()
val handHistories = asyncRealm.where(HandHistory::class.java).findAll()
handHistories.forEach { hh ->
val blinds = arrayListOf(hh.oldSmallBlind, hh.oldBigBlind).filterNotNull()
val blindsFormatted = blinds.map { NumberFormat.getInstance().format(it) }
@ -143,8 +142,8 @@ class Patcher {
private fun patchNegativeLimits() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val sessions = realm.where(Session::class.java).lessThan("limit", 0).findAll()
realm.writeAsync { asyncRealm ->
val sessions = asyncRealm.where(Session::class.java).lessThan("limit", 0).findAll()
sessions.forEach { session ->
session.limit = null
}
@ -154,10 +153,10 @@ class Patcher {
private fun cleanBlindsFilters() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val blindFilterConditions = realm.where(FilterCondition::class.java).equalTo("filterName", "AnyBlind").findAll()
realm.writeAsync { asyncRealm ->
val blindFilterConditions = asyncRealm.where(FilterCondition::class.java).equalTo("filterName", "AnyBlind").findAll()
val filterIds = blindFilterConditions.mapNotNull { it.filters?.firstOrNull() }.map { it.id }
val filters = realm.where(Filter::class.java).`in`("id", filterIds.toTypedArray()).findAll()
val filters = asyncRealm.where(Filter::class.java).`in`("id", filterIds.toTypedArray()).findAll()
filters.deleteAllFromRealm()
}
realm.close()
@ -170,11 +169,12 @@ class Patcher {
private fun patchSessionSet() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.where(SessionSet::class.java).findAll().deleteAllFromRealm()
val sessions = realm.where(Session::class.java).isNotNull("startDate").isNotNull("endDate").findAll()
realm.writeAsync { asyncRealm ->
asyncRealm.where(SessionSet::class.java).findAll().deleteAllFromRealm()
val sessions = asyncRealm.where(Session::class.java)
.isNotNull("startDate").isNotNull("endDate").findAll()
sessions.forEach { session ->
SessionSetManager.updateTimeline(session)
DataManager.updateTimeline(session)
}
}
realm.close()
@ -187,8 +187,8 @@ class Patcher {
*/
private fun patchComputableResults() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val crs = realm.where(ComputableResult::class.java).findAll()
realm.writeAsync { asyncRealm ->
val crs = asyncRealm.where(ComputableResult::class.java).findAll()
crs.forEach { cr ->
cr.session?.let { cr.updateWith(it) }
}
@ -196,13 +196,15 @@ class Patcher {
realm.close()
}
private fun patchPerformances(application: PokerAnalyticsApplication) {
private fun patchPerformances() {
val realm = Realm.getDefaultInstance()
val sessionCount = realm.where<Session>().findAll().size
val performanceCount = realm.where<Performance>().findAll().size
if (sessionCount > 1 && performanceCount == 0) {
application.reportWhistleBlower?.requestReportLaunch()
realm.writeAsync {
DataManager.reportWhistleBlower?.requestReportLaunch()
}
}
realm.close()
}
@ -210,8 +212,8 @@ class Patcher {
private fun patchZeroTable() {
val realm = Realm.getDefaultInstance()
val zero = 0
val sessions = realm.where<Session>().equalTo("numberOfTables", zero).findAll()
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
val sessions = asyncRealm.where<Session>().equalTo("numberOfTables", zero).findAll()
sessions.forEach { s ->
s.numberOfTables = 1
}
@ -221,8 +223,8 @@ class Patcher {
private fun patchRatedAmounts() {
val realm = Realm.getDefaultInstance()
val transactions = realm.where<Transaction>().findAll()
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
val transactions = asyncRealm.where<Transaction>().findAll()
transactions.forEach { t ->
t.computeRatedAmount()
}

@ -1,6 +1,7 @@
package net.pokeranalytics.android.model.migrations
import io.realm.DynamicRealm
import io.realm.DynamicRealmObject
import io.realm.RealmMigration
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import timber.log.Timber
@ -335,6 +336,47 @@ class PokerAnalyticsMigration : RealmMigration {
currentVersion++
}
// Migrate to version 15
if (currentVersion == 14) {
schema.get("Transaction")?.let { ts ->
schema.get("Session")?.let { ss ->
ss.addField("buyin", Double::class.java).setNullable("buyin", true)
.addField("cashout", Double::class.java).setNullable("cashout", true)
.addField("netResult", Double::class.java).setNullable("netResult", true)
.addField("net", Double::class.java)
.addField("tips", Double::class.java).setNullable("tips", true)
.addRealmListField("transactions", ts)
.addField("tournamentFinalPosition", Int::class.java).setNullable("tournamentFinalPosition", true)
.addField("numberOfRebuy", Double::class.java).setNullable("numberOfRebuy", true)
.transform { obj ->
val result = obj.get<DynamicRealmObject>("result")
obj.set("buyin", result.get("buyin"))
obj.set("cashout", result.get("cashout"))
obj.set("netResult", result.get("netResult"))
obj.set("net", result.get("net"))
obj.set("tips", result.get("tips"))
obj.set("tournamentFinalPosition", result.get("tournamentFinalPosition"))
obj.set("numberOfRebuy", result.get("numberOfRebuy"))
obj.set("transactions", result.get("transactions"))
}
}
}
schema.get("CustomFieldEntry")?.let { cfes ->
schema.get("CustomField")?.let { cfs ->
cfes.addRealmObjectField("customField", cfs)
cfs.transform { customField ->
val entries = customField.getList("entries")
for (entry in entries) {
entry.setObject("customField", customField)
}
}
}
}
currentVersion++
}
}
override fun equals(other: Any?): Boolean {

@ -28,12 +28,10 @@ open class ComputableResult : RealmObject(), Filterable {
val rate = session.bankroll?.currency?.rate ?: 1.0
session.result?.let { result ->
this.ratedNet = result.net * rate
this.isPositive = result.isPositive
this.ratedBuyin = (result.buyin ?: 0.0) * rate
this.ratedTips = (result.tips ?: 0.0) * rate
}
this.ratedNet = session.net * rate
this.isPositive = session.isPositive
this.ratedBuyin = (session.buyin ?: 0.0) * rate
this.ratedTips = (session.tips ?: 0.0) * rate
this.bbNet = session.bbNet
this.hasBigBlind = if (session.cgBiggestBet != null) 1 else 0

@ -3,11 +3,13 @@ package net.pokeranalytics.android.model.realm
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.util.UserDefaults
import java.util.*
open class Currency : RealmObject() {
open class Currency : RealmObject(), Identifiable {
companion object {
@ -17,7 +19,10 @@ open class Currency : RealmObject() {
}
@PrimaryKey
var id = UUID.randomUUID().toString()
override var id = UUID.randomUUID().toString()
@Ignore
override val realmObjectClass: Class<out Identifiable> = Currency::class.java
/**
* The currency code of the currency, i.e. USD, EUR...
@ -39,18 +44,23 @@ open class Currency : RealmObject() {
* The rate of the currency with the main currency
*/
var rate: Double? = DEFAULT_RATE
set(value) {
field = value
DataManager.currencyToCompute(this)
}
fun refreshRelatedRatedValues() {
// Timber.d("refreshRelatedRatedValues of $code")
val rate = this.rate ?: DEFAULT_RATE
val query = this.realm.where(ComputableResult::class.java)
query.`in`("session.bankroll.currency.id", arrayOf(this.id))
val cResults = query.findAll()
cResults.forEach { computable ->
computable.session?.result?.net?.let {
computable.session?.net?.let {
computable.ratedNet = it * rate
}
computable.session?.result?.buyin?.let {
computable.session?.buyin?.let {
computable.ratedBuyin = it * rate
}

@ -24,9 +24,8 @@ import net.pokeranalytics.android.ui.view.rows.CustomFieldPropertiesRow
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.SimpleRow
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import timber.log.Timber
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
import kotlin.collections.ArrayList
open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameManageable, StaticRowRepresentableDataSource {
@ -167,10 +166,9 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
return R.string.cf_entry_delete_popup_message
}
override fun deleteDependencies(realm: Realm) {
if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customFields.id", id).findAll()
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll()
entries.deleteAllFromRealm()
}
}
@ -230,6 +228,7 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
*/
fun addEntry(): CustomFieldEntry {
val entry = CustomFieldEntry()
entry.customField = this
this.entries.add(entry)
sortEntries()
updateRowRepresentation()
@ -250,9 +249,11 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
fun cleanupEntries() { // called when saving the custom field
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
this.entriesToDelete.forEach { // entries are out of realm
realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
val ids = this.entriesToDelete.map { it.id }
realm.writeAsync { asyncRealm ->
ids.forEach { id -> // entries are out of realm
asyncRealm.where<CustomFieldEntry>().equalTo("id", id).findFirst()?.deleteFromRealm()
}
}
realm.close()
@ -261,37 +262,16 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
fun getOrCreateEntry(realm: Realm, value: String): CustomFieldEntry {
this.entries.find { it.value == value }?.let {
Timber.d("L>> get")
return it
} ?: run {
Timber.d("L>> create")
val entry = realm.copyToRealm(CustomFieldEntry())
entry.customField = this
entry.value = value
this.entries.add(entry)
return entry
}
}
/**
* Clean the entries if the type is not a list & remove the deleted entries from realm
*/
// fun cleanEntries(realm: Realm) {
// realm.executeTransaction {
//
// if (!isListType) {
// entriesToDelete.addAll(entries)
// entries.clear()
// }
//
// // @TODO
// entriesToDelete.forEach {
// Timber.d("Delete entry: V=${it.value} N=${it.numericValue} / ID=${it.id}")
// realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
// }
// entriesToDelete.clear()
// }
// }
/**
* Returns a comparison criteria based on this custom field
*/

@ -4,9 +4,7 @@ import android.content.Context
import android.text.InputType
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
@ -43,13 +41,14 @@ open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable, R
/**
* The inverse relationship with CustomField
*/
@LinkingObjects("entries")
val customFields: RealmResults<CustomField>? = null
// @LinkingObjects("entries")
// val customFields: RealmResults<CustomField>? = null
val customField: CustomField?
get() {
return this.customFields?.first()
}
var customField: CustomField? = null
// get() {
// return this.customFields?.first()
// }
/**
* The string value of the entry

@ -9,7 +9,10 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.UsageCountable
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.modules.filter.FilterableType
import net.pokeranalytics.android.ui.view.*
@ -40,6 +43,7 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> {
val rootQuery = realm.where<T>()
var realmQuery = query.queryWith(rootQuery)
// Timber.d("entity = ${realmQuery.typeQueried} > desc = ${realmQuery.description}")
sortField?.let {
realmQuery = realmQuery.sort(it)
}
@ -56,18 +60,11 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
override val imageClickable: Boolean?
get() = true
@PrimaryKey
override var id = UUID.randomUUID().toString()
// the queryWith name
var name: String = ""
get() {
if (field.isEmpty()) {
return this.query.defaultName
}
return field
}
override var useCount: Int = 0
@ -109,7 +106,7 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
val previousCondition = filterConditions.filter {
it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
}
filterConditions.removeAll(previousCondition)
filterConditions.removeAll(previousCondition.toSet())
filterConditions.add(newFilterCondition)
}
}
@ -118,7 +115,7 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
fun remove(filterCategoryRow: FilterCategoryRow) {
val sections = filterCategoryRow.filterSectionRows.map { it.name }
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
this.filterConditions.removeAll(savedSections)
this.filterConditions.removeAll(savedSections.toSet())
}
fun countBy(filterCategoryRow: FilterCategoryRow): Int {

@ -2,9 +2,11 @@ package net.pokeranalytics.android.model.realm
import io.realm.Realm
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.LiveOnline
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.view.rows.StaticReport
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.lookupForNameInAllTablesById
@ -16,10 +18,13 @@ interface PerformanceKey {
val value: Int
}
open class Performance() : RealmObject() {
open class Performance() : RealmObject(), Identifiable {
@PrimaryKey
var id: String = UUID.randomUUID().toString()
override var id: String = UUID.randomUUID().toString()
@Ignore
override val realmObjectClass: Class<out Identifiable> = Performance::class.java
constructor(
report: StaticReport,

@ -6,9 +6,9 @@ import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
@ -16,6 +16,7 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.findById
import timber.log.Timber
import java.util.*
@ -67,6 +68,7 @@ open class ReportSetup : RealmObject(), RowRepresentable, Deletable {
*/
fun options(realm: Realm, reportDisplay: ReportDisplay): Calculator.Options {
Timber.d("stats = ${this.statIds.size}")
val stats = this.statIds.map { Stat.valueByIdentifier(it) }
// Comparison criteria

@ -9,12 +9,13 @@ import io.realm.annotations.RealmClass
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
@RealmClass
open class Result : RealmObject(), Filterable {
companion object {
fun fieldNameForQueryType(queryCondition: Class < out QueryCondition>): String? {
fun fieldNameForQueryType(queryCondition: Class <out QueryCondition>): String? {
Session.fieldNameForQueryType(queryCondition)?.let {
return "sessions.$it"
}
@ -22,6 +23,7 @@ open class Result : RealmObject(), Filterable {
}
}
/**
* The buyin amount
*/
@ -29,6 +31,7 @@ open class Result : RealmObject(), Filterable {
set(value) {
field = value
this.computeNumberOfRebuy()
// SessionManager.sessionNetChanged(this.session)
this.computeNet(true)
}
@ -39,9 +42,6 @@ open class Result : RealmObject(), Filterable {
set(value) {
field = value
this.computeNet(true)
if (value != null) {
this.session?.end()
}
}
/**
@ -49,20 +49,8 @@ open class Result : RealmObject(), Filterable {
*/
var netResult: Double? = null
set(value) {
// this.session?.bankroll?.let { bankroll ->
// if (bankroll.live) {
// throw PAIllegalStateException("Can't set net result on a live bankroll")
// }
// } ?: run {
// throw PAIllegalStateException("Session doesn't have any bankroll")
// }
field = value
this.computeNet(false)
if (value != null) {
this.session?.end()
}
}
/**
@ -105,14 +93,14 @@ open class Result : RealmObject(), Filterable {
val isPositive: Int
get() {
return if (session?.isTournament() == true) {
if (this.cashout ?: -1.0 >= 0.0) 1 else 0 // if cashout is null we want to count a negative session
if ((this.cashout ?: -1.0) >= 0.0) 1 else 0 // if cashout is null we want to count a negative session
} else {
if (this.net >= 0.0) 1 else 0
}
}
// Computes the Net
private fun computeNet(withBuyin: Boolean? = null) {
fun computeNet(withBuyin: Boolean? = null) {
val transactionsSum = transactions.sumOf { it.amount }
@ -147,7 +135,7 @@ open class Result : RealmObject(), Filterable {
}
// Computes the number of rebuy
fun computeNumberOfRebuy() {
private fun computeNumberOfRebuy() {
this.session?.let {
if (it.isCashGame()) {
it.cgBiggestBet?.let { bb ->

@ -11,9 +11,9 @@ import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.StatFormattingException
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.Stakes
@ -27,7 +27,6 @@ import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.filter.QueryCondition.*
import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.model.utils.SessionSetManager
import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException
import net.pokeranalytics.android.ui.graph.Graph
import net.pokeranalytics.android.ui.view.*
@ -45,7 +44,7 @@ import java.util.Currency
typealias BB = Double
open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Timed,
open class Session : RealmObject(), Savable, RowRepresentable, Timed,
TimeFilterable, Filterable, DatedBankrollGraphEntry, StakesHolder {
enum class Type(val value: String) {
@ -67,24 +66,23 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
companion object {
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null, managed: Boolean = true): Session {
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null): Session {
val session = Session()
session.result = Result()
if (bankroll != null) {
session.bankroll = bankroll
} else {
session.bankroll = realm.where<Bankroll>().findFirst()
val br: Bankroll? = bankroll ?: realm.where<Bankroll>().findFirst()
br?.let {
session.bankroll = realm.copyFromRealm(br)
}
session.type = if (isTournament) Type.TOURNAMENT.ordinal else Type.CASH_GAME.ordinal
session.type = if (isTournament) Type.TOURNAMENT.ordinal else Type.CASH_GAME.ordinal
session.limit = Limit.NO.ordinal
session.game = realm.where(Game::class.java).equalTo("shortName", "HE").findFirst()
return if (managed) {
realm.copyToRealm(session)
} else {
session
val game = realm.where(Game::class.java).equalTo("shortName", "HE").findFirst()
game?.let {
session.game = realm.copyFromRealm(game)
}
return session
}
fun fieldNameForQueryType(queryCondition: Class < out QueryCondition >): String? {
@ -102,9 +100,9 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
AnyStake::class.java -> "cgStakes"
NumberOfTable::class.java -> "numberOfTables"
NetAmountWon::class.java, NetAmountLost::class.java -> "computableResults.ratedNet"
NumberOfRebuy::class.java -> "result.numberOfRebuy"
NumberOfRebuy::class.java -> "numberOfRebuy"
TournamentNumberOfPlayer::class.java -> "tournamentNumberOfPlayers"
TournamentFinalPosition::class.java -> "result.tournamentFinalPosition"
TournamentFinalPosition::class.java -> "tournamentFinalPosition"
TournamentFee::class.java -> "tournamentEntryFee"
StartedFromDate::class.java, StartedToDate::class.java, EndedFromDate::class.java, EndedToDate::class.java -> "startDate"
AnyDayOfWeek::class.java, IsWeekEnd::class.java, IsWeekDay::class.java -> "dayOfWeek"
@ -118,7 +116,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
Duration::class.java -> "netDuration"
CustomFieldListQuery::class.java -> "customFieldEntries.id"
CustomFieldAmountQuery::class.java, CustomFieldNumberQuery::class.java -> "customFieldEntries.numericValue"
CustomFieldQuery::class.java -> "customFieldEntries.customFields.id"
CustomFieldQuery::class.java -> "customFieldEntries.customField.id"
DateNotNull::class.java -> "startDate"
EndDateNotNull::class.java -> "endDate"
BiggestBetNotNull::class.java -> "cgBiggestBet"
@ -148,7 +146,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val sessionType: Type
get() { return Type.values()[this.type] }
// The result of the main user
// Not used anymore
var result: Result? = null
@LinkingObjects("session")
@ -209,7 +207,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.endDate = null
}
this.dateChanged()
this.computeStats()
this.addToComputeQueue()
}
/**
@ -230,7 +228,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.computeNetDuration()
this.dateChanged()
this.defineDefaultTournamentBuyinIfNecessary()
this.computeStats()
this.addToComputeQueue()
}
/**
@ -240,7 +238,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
set(value) {
field = value
this.computeNetDuration()
this.computeStats()
this.addToComputeQueue()
}
/**
@ -252,10 +250,6 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* The start date of the break
*/
override var pauseDate: Date? = null
set(value) {
field = value
// this.updateRowRepresentation()
}
// The session set containing the sessions, which can contain multiple endedSessions
var sessionSet: SessionSet? = null
@ -268,7 +262,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
set(value) {
field = value
this.generateStakes()
this.computeStats()
this.addToComputeQueue()
// this.updateRowRepresentation()
}
@ -296,7 +290,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
set(value) {
if (value > 0) {
field = value
this.computeStats()
this.addToComputeQueue()
}
}
@ -314,16 +308,13 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
// The small blind value
var cgOldSmallBlind: Double? = null
set(value) {
field = value
}
// The big blind value
var cgOldBigBlind: Double? = null
set(value) {
field = value
this.computeStats()
this.result?.computeNumberOfRebuy()
this.addToComputeQueue()
this.computeNumberOfRebuy()
}
// var blinds: String? = null
@ -334,8 +325,8 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
field = value
this.generateStakes()
this.defineHighestBet()
this.computeStats()
this.result?.computeNumberOfRebuy()
this.addToComputeQueue()
this.computeNumberOfRebuy()
}
var cgBlinds: String? = null
@ -343,8 +334,8 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
field = cleanupBlinds(value)
this.generateStakes()
this.defineHighestBet()
this.computeStats()
this.result?.computeNumberOfRebuy()
this.addToComputeQueue()
this.computeNumberOfRebuy()
}
var cgBiggestBet: Double? = null
@ -357,7 +348,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
var tournamentEntryFee: Double? = null
set(value) {
field = value
this.result?.computeNumberOfRebuy()
this.computeNumberOfRebuy()
}
// The total number of players who participated in the tournament
@ -379,9 +370,131 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
var handsCount: Int? = null
set(value) {
field = value
this.computeStats()
this.addToComputeQueue()
}
/**
* The buyin amount
*/
var buyin: Double? = null
set(value) {
field = value
this.computeNumberOfRebuy()
this.computeNet(true)
}
/**
* The cashed out amount
*/
var cashout: Double? = null
set(value) {
field = value
this.computeNet(true)
}
/**
* The net result
*/
var netResult: Double? = null
set(value) {
field = value
this.computeNet(false)
}
/**
* The pre-computed net (readonly)
*/
var net: Double = 0.0
private set
/**
* Tips
*/
var tips: Double? = null
set(value) {
field = value
this.addToComputeQueue()
}
// The transactions associated with the Result, impacting the result
var transactions: RealmList<Transaction> = RealmList()
set(value) {
field = value
this.computeNet()
}
// The tournament final position, if applicable
var tournamentFinalPosition: Int? = null
// Number of rebuys
var numberOfRebuy: Double? = null
//////////////////////////////
// Computes the Net
fun computeNet(withBuyin: Boolean? = null) {
val transactionsSum = transactions.sumOf { it.amount }
// choose the method to compute the net
var useBuyin = withBuyin ?: true
if (withBuyin == null) {
if (netResult != null) {
useBuyin = false
} else if (buyin != null || cashout != null) {
useBuyin = true
} else {
if (this.isCashGame() && !this.isLive) {
useBuyin = false
}
}
}
if (useBuyin) {
val buyin = this.buyin ?: 0.0
val cashOut = this.cashout ?: 0.0
this.net = cashOut - buyin + transactionsSum
} else {
val netResult = this.netResult ?: 0.0
this.net = netResult + transactionsSum
}
// Precompute results
this.addToComputeQueue()
}
// Computes the number of rebuy
fun computeNumberOfRebuy() {
if (this.isCashGame()) {
this.cgBiggestBet?.let { bb ->
if (bb > 0.0) {
this.numberOfRebuy = (this.buyin ?: 0.0) / (bb * 100.0)
} else {
this.numberOfRebuy = null
}
}
} else {
this.tournamentEntryFee?.let { entryFee ->
if (entryFee > 0.0) {
this.numberOfRebuy = (this.buyin ?: 0.0) / entryFee
} else {
this.numberOfRebuy = null
}
}
}
}
val isPositive: Int
get() {
return if (this.isTournament()) {
if ((this.cashout ?: -1.0) >= 0.0) 1 else 0 // if cashout is null we want to count a negative session
} else {
if (this.net >= 0.0) 1 else 0
}
}
//////////////////////////////
fun bankrollHasBeenUpdated() {
this.generateStakes()
}
@ -391,11 +504,15 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Should be called when the start / end date are changed
*/
private fun dateChanged() {
if (this.endDate != null) {
SessionSetManager.updateTimeline(this)
} else if (this.sessionSet != null) {
SessionSetManager.removeFromTimeline(this)
}
DataManager.sessionDateChanged(this)
// if (this.endDate != null) {
// SessionSetManager.updateTimeline(this)
// } else if (this.sessionSet != null) {
// SessionSetManager.removeFromTimeline(this)
// }
// this.updateRowRepresentation()
}
@ -435,9 +552,9 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val bbNet: BB
get() {
val bb = this.cgBiggestBet
val result = this.result
return if (bb != null && result != null) {
result.net / bb
// val result = this.result
return if (bb != null) {
this.net / bb
} else {
0.0
}
@ -468,9 +585,16 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
@Ignore
override var amount: Double = 0.0
get() {
return this.result?.net ?: 0.0
return this.net
}
/**
* Pre-compute various statIds
*/
private fun addToComputeQueue() {
DataManager.sessionToCompute(this)
}
/**
* Pre-compute various statIds
*/
@ -501,17 +625,13 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val numberOfHandsPerHour: Double
get() {
val tableSize = this.tableSize ?: 9 // 9 is the default table size if null
val config = UserConfig.getConfiguration(this.realm)
val playerHandsPerHour = if (this.isLive) config.liveDealtHandsPerHour else config.onlineDealtHandsPerHour
return this.numberOfTables * playerHandsPerHour / tableSize.toDouble()
val dealtHandsPerHour = if (this.isLive) UserConfigObserver.liveDealtHandsPerHour else UserConfigObserver.onlineDealtHandsPerHour
return this.numberOfTables * dealtHandsPerHour / tableSize.toDouble()
}
val hourlyRate: Double
get() {
this.result?.let { result ->
return result.net / this.hourlyDuration
}
throw ModelException("Session should have an existing Result relationship")
return this.net / this.hourlyDuration
}
private val bbHourlyRate: BB
@ -529,12 +649,12 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val hasBuyin: Boolean
get() {
return this.result?.buyin != null
return this.buyin != null
}
val hasNetResult: Boolean
get() {
return this.result?.netResult != null
return this.netResult != null
}
// Manageable
@ -557,31 +677,29 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Start or continue a session
*/
fun startOrContinue() {
realm.executeTransaction {
when (val state = getState()) {
SessionState.PENDING, SessionState.PLANNED -> {
this.startDate = Date()
this.defineDefaultTournamentBuyinIfNecessary()
}
SessionState.PAUSED -> {
val pauseDate = this.pauseDate
if (pauseDate != null) {
this.breakDuration += Date().time - pauseDate.time
} else {
throw PAIllegalStateException("When resuming, the pause date must be set")
}
this.pauseDate = null
}
else -> {
throw PAIllegalStateException("unmanaged session state: $state")
when (val state = getState()) {
SessionState.PENDING, SessionState.PLANNED -> {
this.startDate = Date()
this.defineDefaultTournamentBuyinIfNecessary()
}
SessionState.PAUSED -> {
val pauseDate = this.pauseDate
if (pauseDate != null) {
this.breakDuration += Date().time - pauseDate.time
} else {
throw PAIllegalStateException("When resuming, the pause date must be set")
}
this.pauseDate = null
}
else -> {
throw PAIllegalStateException("unmanaged session state: $state")
}
}
}
private fun defineDefaultTournamentBuyinIfNecessary() {
if (this.tournamentEntryFee != null && this.result?.buyin == null) {
this.result?.buyin = this.tournamentEntryFee
if (this.tournamentEntryFee != null && this.buyin == null) {
this.buyin = this.tournamentEntryFee
}
}
@ -589,13 +707,11 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Pause a session
*/
fun pause() {
realm.executeTransaction {
when (val state = getState()) {
SessionState.STARTED -> {
this.pauseDate = Date()
}
else -> throw PAIllegalStateException("Pausing a session in an unmanaged state: $state")
when (val state = getState()) {
SessionState.STARTED -> {
this.pauseDate = Date()
}
else -> throw PAIllegalStateException("Pausing a session in an unmanaged state: $state")
}
}
@ -603,13 +719,11 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Stop a session
*/
fun stop(context: Context) {
realm.executeTransaction {
when (val state = getState()) {
SessionState.STARTED, SessionState.PAUSED -> {
this.end()
}
else -> throw Exception("Stopping session in unmanaged state: $state")
when (val state = getState()) {
SessionState.STARTED, SessionState.PAUSED -> {
this.end()
}
else -> throw Exception("Stopping session in unmanaged state: $state")
}
cancelStopNotification(context)
}
@ -618,12 +732,10 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Restart a session
*/
fun restart() {
realm.executeTransaction {
this.pauseDate = null
this.startDate = Date()
this.endDate = null
this.breakDuration = 0L
}
this.pauseDate = null
this.startDate = Date()
this.endDate = null
this.breakDuration = 0L
}
/**
@ -688,10 +800,11 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
if (isValid) {
// CrashLogging.log("Deletes session. Id = ${this.id}")
realm.executeTransaction {
// realm.executeTransaction {
cleanup()
deleteFromRealm()
}
// }
} else {
CrashLogging.log("Attempt to delete an invalid session")
}
@ -704,10 +817,10 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
// Updates the timeline
this.sessionSet?.let {
SessionSetManager.removeFromTimeline(this)
DataManager.removeFromTimeline(this)
}
// cleanup unnecessary related objects
this.result?.deleteFromRealm()
// this.deleteFromRealm()
this.computableResults?.deleteAllFromRealm()
}
@ -738,7 +851,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
return "Session ${this.creationDate}"
}
override fun updateValue(value: Any?, row: RowRepresentable) {
fun updateValue(value: Any?, row: RowRepresentable) {
when (row) {
SessionPropertiesRow.BANKROLL -> bankroll = value as Bankroll?
@ -757,38 +870,50 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.breakDuration = (value as Double? ?: 0.0).toLong() * 60 * 1000
}
SessionPropertiesRow.BUY_IN -> {
val localResult = getOrCreateResult()
localResult.buyin = value as Double?
// val localResult = getOrCreateResult()
this.buyin = value as Double?
}
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> {
val localResult = getOrCreateResult()
localResult.cashout = value as Double?
// val localResult = getOrCreateResult()
val cashOut = value as Double?
this.cashout = cashOut
if (cashOut != null) {
this.end()
}
}
SessionPropertiesRow.NET_RESULT -> {
val localResult = getOrCreateResult()
localResult.netResult = value as Double?
// val localResult = getOrCreateResult()
val netResult = value as Double?
this.netResult = netResult
if (netResult != null) {
this.end()
}
}
SessionPropertiesRow.COMMENT -> comment = value as String? ?: ""
SessionPropertiesRow.END_DATE -> if (value is Date?) {
this.endDate = value
}
SessionPropertiesRow.GAME -> {
if (value is ArrayList<*>) {
limit = try {
(value[0] as Int?)
} catch (e: Exception) {
null
when (value) {
is ArrayList<*> -> {
limit = try {
(value[0] as Int?)
} catch (e: Exception) {
null
}
game = try {
(value[1] as Game?)
} catch (e: Exception) {
null
}
}
game = try {
(value[1] as Game?)
} catch (e: Exception) {
null
is Game -> {
game = value
}
null -> {
limit = null
game = null
}
} else if (value is Game) {
game = value
} else if (value == null) {
limit = null
game = null
}
}
SessionPropertiesRow.INITIAL_BUY_IN -> {
@ -803,21 +928,21 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
}
SessionPropertiesRow.POSITION -> {
val localResult = if (result != null) result as Result else realm.createObject(Result::class.java)
// val localResult = if (result != null) result as Result else realm.createObject(Result::class.java)
if (value is Double) {
localResult.tournamentFinalPosition = value.toInt()
this.tournamentFinalPosition = value.toInt()
} else {
localResult.tournamentFinalPosition = null
this.tournamentFinalPosition = null
}
result = localResult
// result = localResult
}
SessionPropertiesRow.START_DATE -> if (value is Date) {
this.startDate = value
}
SessionPropertiesRow.TABLE_SIZE -> tableSize = value as Int?
SessionPropertiesRow.TIPS -> {
val localResult = getOrCreateResult()
localResult.tips = value as Double?
// val localResult = getOrCreateResult()
this.tips = value as Double?
}
SessionPropertiesRow.TOURNAMENT_NAME -> tournamentName = value as TournamentName?
SessionPropertiesRow.TOURNAMENT_TYPE -> tournamentType = (value as TournamentType?)?.ordinal
@ -842,6 +967,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
if (value != null) {
val customFieldEntry = CustomFieldEntry()
customFieldEntry.numericValue = value as Double?
customFieldEntry.customField = row
customFieldEntries.add(customFieldEntry)
row.entries.add(customFieldEntry)
}
@ -857,14 +983,25 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
private fun getOrCreateResult(): Result {
return this.result
?: run {
val result = realm.createObject(Result::class.java)
this.result = result
result
}
}
// private fun customFieldEntries(realm: Realm, customField: CustomField): List<CustomFieldEntry> {
//
//// val cfEntries = customField.entries
//// val sessionEntries = this.customFieldEntries
////
//// val entries = realm.where<CustomFieldEntry>()
//// .`in`()
//
// return listOf()
// }
// private fun getOrCreateResult(): Result {
// return this.result
// ?: run {
// val result = realm.createObject(Result::class.java)
// this.result = result
// result
// }
// }
// Stat Entry
@ -879,40 +1016,34 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
override fun formattedValue(stat: Stat): TextFormat {
this.result?.let { result ->
val value: Double? = when (stat) {
Stat.NET_RESULT, Stat.AVERAGE, Stat.STANDARD_DEVIATION -> result.net
Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> 1.0
Stat.AVERAGE_BUYIN -> result.buyin
Stat.ROI -> {
result.buyin?.let {
Stat.returnOnInvestment(result.net, it)
} ?: run {
null
}
val value: Double? = when (stat) {
Stat.NET_RESULT, Stat.AVERAGE, Stat.STANDARD_DEVIATION -> this.net
Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> 1.0
Stat.AVERAGE_BUYIN -> this.buyin
Stat.ROI -> {
this.buyin?.let {
Stat.returnOnInvestment(this.net, it)
} ?: run {
null
}
Stat.HOURLY_RATE_BB -> this.bbHourlyRate
Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> Stat.netBBPer100Hands(
this.bbNet,
this.estimatedHands
)
Stat.AVERAGE_NET_BB, Stat.BB_NET_RESULT -> this.bbNet
Stat.HOURLY_DURATION, Stat.AVERAGE_HOURLY_DURATION -> this.netDuration.toDouble()
Stat.HOURLY_RATE, Stat.STANDARD_DEVIATION_HOURLY -> this.hourlyRate
Stat.HANDS_PLAYED -> this.estimatedHands
Stat.WIN_RATIO -> null
else -> throw StatFormattingException("format undefined for stat ${stat.name}")
}
value?.let {
return stat.textFormat(it, currency = currency)
} ?: run {
return TextFormat(NULL_TEXT)
}
Stat.HOURLY_RATE_BB -> this.bbHourlyRate
Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> Stat.netBBPer100Hands(
this.bbNet,
this.estimatedHands
)
Stat.AVERAGE_NET_BB, Stat.BB_NET_RESULT -> this.bbNet
Stat.HOURLY_DURATION, Stat.AVERAGE_HOURLY_DURATION -> this.netDuration.toDouble()
Stat.HOURLY_RATE, Stat.STANDARD_DEVIATION_HOURLY -> this.hourlyRate
Stat.HANDS_PLAYED -> this.estimatedHands
Stat.WIN_RATIO -> null
else -> throw StatFormattingException("format undefined for stat ${stat.name}")
}
value?.let {
return stat.textFormat(it, currency = currency)
} ?: run {
throw PAIllegalStateException("Asking for statIds on Session without Result")
return TextFormat(NULL_TEXT)
}
}
@ -965,25 +1096,25 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
@Ignore
override val realmObjectClass: Class<out Identifiable> = Session::class.java
fun charSequenceForRow(row: RowRepresentable, context: Context): String {
fun charSequenceForRow(row: RowRepresentable, context: Context, realm: Realm): String {
return when (row) {
SessionPropertiesRow.BANKROLL -> bankroll?.name ?: NULL_TEXT
SessionPropertiesRow.STAKES -> getFormattedStakes()
SessionPropertiesRow.BREAK_TIME -> if (this.breakDuration > 0.0) this.breakDuration.toMinutes() else NULL_TEXT
SessionPropertiesRow.BUY_IN -> this.result?.buyin?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> this.result?.cashout?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.NET_RESULT -> this.result?.netResult?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.BUY_IN -> this.buyin?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> this.cashout?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.NET_RESULT -> this.netResult?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.COMMENT -> if (this.comment.isNotEmpty()) this.comment else NULL_TEXT
SessionPropertiesRow.END_DATE -> this.endDate?.shortDateTime() ?: NULL_TEXT
SessionPropertiesRow.GAME -> getFormattedGame()
SessionPropertiesRow.INITIAL_BUY_IN -> tournamentEntryFee?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.LOCATION -> location?.name ?: NULL_TEXT
SessionPropertiesRow.PLAYERS -> tournamentNumberOfPlayers?.toString() ?: NULL_TEXT
SessionPropertiesRow.POSITION -> result?.tournamentFinalPosition?.toString() ?: NULL_TEXT
SessionPropertiesRow.POSITION -> this.tournamentFinalPosition?.toString() ?: NULL_TEXT
SessionPropertiesRow.START_DATE -> this.startDate?.shortDateTime() ?: NULL_TEXT
SessionPropertiesRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT
SessionPropertiesRow.TIPS -> result?.tips?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.TIPS -> this.tips?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.TOURNAMENT_TYPE -> {
this.tournamentType?.let {
TournamentType.values()[it].localizedTitle(context)
@ -1005,10 +1136,14 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
}
SessionPropertiesRow.TOURNAMENT_NAME -> tournamentName?.name ?: NULL_TEXT
SessionPropertiesRow.HANDS -> this.handHistories?.size.toString()
SessionPropertiesRow.HANDS -> {
val handHistories = realm.where(HandHistory::class.java).equalTo("session.id", this.id).findAll()
handHistories.size.toString()
}
SessionPropertiesRow.HANDS_COUNT -> this.handsCountFormatted(context)
SessionPropertiesRow.NUMBER_OF_TABLES -> this.numberOfTables.toString()
is CustomField -> {
// Timber.d("entries count = ${customFieldEntries.size}")
customFieldEntries.find { it.customField?.id == row.id }?.let { customFieldEntry ->
return customFieldEntry.getFormattedValue(currency)
}
@ -1024,12 +1159,12 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
fun clearBuyinCashedOut() {
this.result?.buyin = null
this.result?.cashout = null
this.buyin = null
this.cashout = null
}
fun clearNetResult() {
this.result?.netResult = null
this.netResult = null
}
private fun cleanupBlinds(blinds: String?): String? {

@ -19,7 +19,7 @@ import java.text.DateFormat
import java.util.*
open class SessionSet() : RealmObject(), Timed, Filterable {
open class SessionSet : RealmObject(), Timed, Filterable {
@PrimaryKey
override var id = UUID.randomUUID().toString()
@ -61,6 +61,7 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
override var netDuration: Long = 0L
fun computeStats() {
// Timber.d("compute > session count = ${this.sessions?.size}")
this.ratedNet = this.sessions?.sumOf { it.computableResult?.ratedNet ?: 0.0 } ?: 0.0
this.estimatedHands = this.sessions?.sumOf { it.estimatedHands } ?: 0.0
this.bbNet = this.sessions?.sumOf { it.bbNet } ?: 0.0
@ -75,7 +76,7 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
var ratedNet: Double = 0.0
val hourlyRate: Double
private val hourlyRate: Double
get() {
return this.ratedNet / this.hourlyDuration
}
@ -84,7 +85,7 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
var bbNet: BB = 0.0
val bbHourlyRate: BB
private val bbHourlyRate: BB
get() {
return this.bbNet / this.hourlyDuration
}
@ -100,14 +101,13 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
companion object {
fun newInstance(realm: Realm) : SessionSet {
val sessionSet = SessionSet()
return realm.copyToRealm(sessionSet)
fun newInstance(realm: Realm): SessionSet {
return realm.copyToRealm(SessionSet())
}
fun fieldNameForQueryType(queryCondition: Class < out QueryCondition >): String? {
Session.fieldNameForQueryType(queryCondition)?.let {
return "sessions.$it"
fun fieldNameForQueryType(queryCondition: Class <out QueryCondition>): String? {
Session.fieldNameForQueryType(queryCondition)?.let { fieldName ->
return "sessions.$fieldName"
}
return null
}

@ -2,15 +2,18 @@ package net.pokeranalytics.android.model.realm
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.util.UUID_SEPARATOR
import net.pokeranalytics.android.util.extensions.findById
import timber.log.Timber
import java.util.*
open class UserConfig : RealmObject() {
companion object {
// these values are buffered to avoid the use of Realm instance
fun getConfiguration(realm: Realm): UserConfig {
realm.where(UserConfig::class.java).findFirst()?.let { config ->
return config
@ -29,8 +32,12 @@ open class UserConfig : RealmObject() {
var transactionTypeIds: String = ""
fun setTransactionTypeIds(transactionTypes: Set<TransactionType>) {
this.transactionTypeIds = transactionTypes.joinToString(UUID_SEPARATOR) { it.id }
// fun setTransactionTypeIds(transactionTypes: Set<TransactionType>) {
// this.transactionTypeIds = transactionTypes.joinToString(UUID_SEPARATOR) { it.id }
// }
fun setTransactionTypeIds(transactionTypeIds: Set<String>) {
this.transactionTypeIds = transactionTypeIds.joinToString(UUID_SEPARATOR)
}
fun transactionTypes(realm: Realm): List<TransactionType> {
@ -38,4 +45,40 @@ open class UserConfig : RealmObject() {
return ids.mapNotNull { realm.findById(it) }
}
}
}
object UserConfigObserver {
private var userConfig: RealmResults<UserConfig>? = null
var liveDealtHandsPerHour: Int = 0
var onlineDealtHandsPerHour: Int = 0
init {
val realm = Realm.getDefaultInstance()
this.updateValues(realm)
this.userConfig = realm.where(UserConfig::class.java).findAll()
this.userConfig?.addChangeListener { results ->
this.updateValues(results.realm)
val userConfig = UserConfig.getConfiguration(results.realm)
this.liveDealtHandsPerHour = userConfig.liveDealtHandsPerHour
this.onlineDealtHandsPerHour = userConfig.onlineDealtHandsPerHour
}
realm.close()
}
fun create() { }
private fun updateValues(realm: Realm) {
val userConfig = UserConfig.getConfiguration(realm)
this.liveDealtHandsPerHour = userConfig.liveDealtHandsPerHour
this.onlineDealtHandsPerHour = userConfig.onlineDealtHandsPerHour
Timber.d("UserConfigObserver values updated: live = ${this.liveDealtHandsPerHour}, online = ${this.onlineDealtHandsPerHour}")
}
}

@ -198,8 +198,16 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
handSetup.ante?.let { this.ante = it }
handSetup.blinds?.let { this.blinds = it }
this.session = handSetup.session
this.date = this.session?.handHistoryAutomaticDate ?: Date()
handSetup.session?.let { session ->
this.date = session.handHistoryAutomaticDate
session.realm?.let { realm ->
this.session = realm.copyFromRealm(session)
} ?: run {
this.session = session
}
} ?: run {
this.date = Date()
}
this.createActions(handSetup)
}
@ -313,13 +321,7 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
* Creates and affect a PlayerSetup at the given [positionIndex]
*/
fun createPlayerSetup(positionIndex: Int): PlayerSetup {
val playerSetup = if (this.realm != null) {
this.realm.createObject(PlayerSetup::class.java) }
else {
PlayerSetup()
}
val playerSetup = PlayerSetup()
playerSetup.position = positionIndex
this.playerSetups.add(playerSetup)
return playerSetup

@ -16,7 +16,7 @@ class DataUtils {
fun sessionCount(realm: Realm, startDate: Date, endDate: Date?, net: Double): Int {
var sessionQuery = realm.where(Session::class.java)
.equalTo("startDate", startDate)
.equalTo("result.net", net)
.equalTo("net", net)
endDate?.let {
sessionQuery = sessionQuery.equalTo("endDate", it)

@ -11,11 +11,11 @@ import net.pokeranalytics.android.ui.view.rows.SessionPropertiesRow
* Returns all significant parameters concatenated in a String
* Not suitable for display
*/
private fun Session.parameterRepresentation(context: Context): String {
private fun Session.parameterRepresentation(context: Context, realm: Realm): String {
var representation = ""
this.significantFields().forEach {
representation += this.charSequenceForRow(it, context)
representation += this.charSequenceForRow(it, context, realm)
}
return representation
@ -73,15 +73,20 @@ class FavoriteSessionFinder {
/**
* Copies the favorite session parameters on the [session]
*/
fun copyParametersFromFavoriteSession(session: Session, location: Location?, context: Context) {
fun copyParametersFromFavoriteSession(session: Session, location: Location?, context: Context, realm: Realm) {
val favoriteSession = favoriteSession(session.type, location, session.realm, context)
val favoriteSession = favoriteSession(session.type, location, realm, context)
favoriteSession?.let { fav ->
session.limit = fav.limit
session.game = fav.game
session.bankroll = fav.bankroll
fav.game?.let { game ->
session.game = realm.copyFromRealm(game)
}
fav.bankroll?.let { br ->
session.bankroll = realm.copyFromRealm(br)
}
session.tableSize = fav.tableSize
when (session.type) {
@ -112,7 +117,7 @@ class FavoriteSessionFinder {
val counters = hashMapOf<String, Counter>()
lastSessions.forEach { session ->
val representation = session.parameterRepresentation(context)
val representation = session.parameterRepresentation(context, realm)
val counter = counters[representation]
if (counter != null) {
counter.increment()

@ -1,209 +0,0 @@
package net.pokeranalytics.android.model.utils
import io.realm.RealmQuery
import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet
import kotlin.math.max
class CorruptSessionSetException(message: String) : Exception(message)
/**
* The manager is in charge of updating the abstract concept of timeline,
* representing the sequenced time frames where the user plays.
*/
class SessionSetManager {
companion object {
/**
* Updates the global timeline using the updated [session]
*/
fun updateTimeline(session: Session) {
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
if (session.startDate == null) {
throw ModelException("Start date should never be null here")
}
if (session.endDate == null) {
throw ModelException("End date should never be null here")
}
val sessionSets = this.matchingSets(session)
cleanupSessionSets(session, sessionSets)
}
private fun matchingSets(session: Session) : RealmResults<SessionSet> {
val realm = session.realm
val endDate = session.endDate!! // tested above
val startDate = session.startDate!!
val query: RealmQuery<SessionSet> = 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)
return query.findAll()
}
/**
* Multiple session sets update:
* Merges or splits session sets
* Does that by deleting then recreating
*/
private fun cleanupSessionSets(session: Session, sessionSets: RealmResults<SessionSet>) {
// get all endedSessions from sets
val allImpactedSessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { allImpactedSessions.addAll(it) }
}
allImpactedSessions.add(session)
// delete all sets
sessionSets.deleteAllFromRealm()
allImpactedSessions.forEach { impactedSession ->
val sets = matchingSets(impactedSession)
this.updateTimeFrames(sets, impactedSession)
}
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Update the global timeline using the impacted [sessionSets] and the updated [session]
*/
private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, session: Session) {
when (sessionSets.size) {
0 -> this.createOrUpdateSessionSet(session)
else -> this.mergeSessionGroups(session, sessionSets)
}
}
/**
* Creates or update the session set for the [session]
*/
private fun createOrUpdateSessionSet(session: Session) {
val set = session.sessionSet
if (set != null) {
set.startDate = session.startDate!! // tested above
set.endDate = session.endDate!!
} else {
this.createSessionSet(session)
}
}
/**
* Create a set and affect it to the [session]
*/
private fun createSessionSet(session: Session) {
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = session.startDate!!
set.endDate = session.endDate!!
set.breakDuration = session.breakDuration
session.sessionSet = set
set.computeStats()
}
/**
* Multiple session sets update:
* Merges all sets into one (delete all then create a new one)
*/
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) {
var startDate = session.startDate!!
var endDate = session.endDate!!
// get all endedSessions from sets
val sessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { sessions.addAll(it) }
}
// find earlier and later dates from all sets
sessions.forEach { s ->
if (s.startDate != null && s.endDate != null) {
val start = s.startDate!!
val end = s.endDate!!
if (start.before(startDate)) {
startDate = start
}
if (end.after(endDate)) {
endDate = end
}
} else {
throw CorruptSessionSetException("Set contains unfinished sessions!")
}
}
// delete all sets
sessionSets.deleteAllFromRealm()
// Create a new set
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = startDate
set.endDate = endDate
// Add the session linked to this timeframe to the new sessionGroup
session.sessionSet = set
// Add all orphan endedSessions
sessions.forEach { s ->
s.sessionSet = set
set.breakDuration = max(set.breakDuration, s.breakDuration)
}
set.computeStats()
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Removes the [session] from the timeline
*/
fun removeFromTimeline(session: Session) {
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
val sessionSet = session.sessionSet
if (sessionSet != null) {
val sessions = mutableSetOf<Session>()
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) }
sessions.remove(session)
sessionSet.deleteFromRealm()
sessions.forEach {
updateTimeline(it)
}
}
}
}
}

@ -6,14 +6,13 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import io.realm.RealmResults
import net.pokeranalytics.android.BuildConfig
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.NewPerformanceListener
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.databinding.ActivityHomeBinding
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.Currency
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.activity.components.BaseActivity
import net.pokeranalytics.android.ui.adapter.HomePagerAdapter
@ -41,13 +40,12 @@ class HomeActivity : BaseActivity(), NewPerformanceListener {
}
}
private lateinit var currencies: RealmResults<Currency>
private var homePagerAdapter: HomePagerAdapter? = null
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
if (binding.viewPager.currentItem == Tab.REPORTS.identifier) {
this.paApplication.reportWhistleBlower?.clearNotifications()
DataManager.reportWhistleBlower?.clearNotifications()
}
when (item.itemId) {
@ -95,32 +93,16 @@ class HomeActivity : BaseActivity(), NewPerformanceListener {
}
}
observeRealmObjects()
initUI()
checkFirstLaunch()
this.paApplication.reportWhistleBlower?.addListener(this)
DataManager.reportWhistleBlower?.addListener(this)
}
override fun onDestroy() {
super.onDestroy()
this.paApplication.reportWhistleBlower?.removeListener(this)
}
private fun observeRealmObjects() {
val realm = getRealm()
// observe currency changes
this.currencies = realm.where(Currency::class.java).findAll()
this.currencies.addChangeListener { currencies, _ ->
realm.executeTransaction {
currencies.forEach {
it.refreshRelatedRatedValues()
}
}
}
DataManager.reportWhistleBlower?.removeListener(this)
}
/**
@ -163,12 +145,10 @@ class HomeActivity : BaseActivity(), NewPerformanceListener {
}
override fun newBestPerformanceHandler() {
if (Preferences.showInAppBadges(this)) {
binding.navigation.getOrCreateBadge(R.id.navigation_reports).isVisible = true
binding.navigation.getOrCreateBadge(R.id.navigation_reports).number = 1
}
}
private fun lookForCalendarBadge() {

@ -9,14 +9,13 @@ import android.provider.MediaStore
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.util.ImageUtils
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.util.*
open class MediaActivity : BaseActivity() {
@ -52,12 +51,12 @@ open class MediaActivity : BaseActivity() {
val filesList = ArrayList<File>()
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
if (tempFile != null) {
tempFile?.let {
GlobalScope.launch(Dispatchers.Main) {
filesList.add(it)
CoroutineScope(context = Dispatchers.Main).launch {
filesList.add(it)
getPictures(filesList)
}
}
@ -65,7 +64,7 @@ open class MediaActivity : BaseActivity() {
data.clipData?.let { clipData ->
try {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
isLoadingNewPictures()
}
@ -78,7 +77,7 @@ open class MediaActivity : BaseActivity() {
filesList.add(photoFile)
}
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
getPictures(filesList)
}
@ -90,7 +89,7 @@ open class MediaActivity : BaseActivity() {
data.data?.let { uri ->
try {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
isLoadingNewPictures()
}
@ -98,7 +97,7 @@ open class MediaActivity : BaseActivity() {
val photoFile = ImageUtils.createTempImageFile(this@MediaActivity)
ImageUtils.copyInputStreamToFile(inputStream!!, photoFile)
filesList.add(photoFile)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
getPictures(filesList)
}
@ -176,13 +175,6 @@ open class MediaActivity : BaseActivity() {
}
}
// // Test if we have the permission
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// selectedChoice = SELECTED_CHOICE_TAKE_PICTURE
// askForStoragePermission()
// return
// }
}
@ -206,21 +198,6 @@ open class MediaActivity : BaseActivity() {
}
}
// Test if we have the permission
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// selectedChoice = SELECTED_CHOICE_SELECT_PICTURE
// askForStoragePermission()
// return
// }
//
// this.multiplePictures = multiplePictures
//
// val galleryIntent = Intent()
// galleryIntent.type = "image/*"
// galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiplePictures)
// galleryIntent.action = Intent.ACTION_GET_CONTENT
// startActivityForResult(galleryIntent, REQUEST_CODE_SELECT_PICTURE)
}
/**

@ -8,10 +8,11 @@ import android.view.ViewGroup
import android.widget.TextView
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.databinding.FragmentImportBinding
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.util.csv.CSVImporter
@ -63,7 +64,6 @@ class ImportFragment : RealmFragment(), ImportDelegate {
super.onViewCreated(view, savedInstanceState)
this.initUI()
this.startImport()
}
@ -91,14 +91,14 @@ class ImportFragment : RealmFragment(), ImportDelegate {
private fun startImport() {
this.parentActivity?.paApplication?.reportWhistleBlower?.pause()
DataManager.reportWhistleBlower?.pause()
this.importer = CSVImporter(uri, requireContext())
this.importer.delegate = this
CoroutineScope(coroutineContext).launch {
val coroutine = GlobalScope.async {
val coroutine = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>> Start Import...")
@ -142,7 +142,7 @@ class ImportFragment : RealmFragment(), ImportDelegate {
}
private fun end() {
this.parentActivity?.paApplication?.reportWhistleBlower?.resume()
DataManager.reportWhistleBlower?.resume()
activity?.finish()
}

@ -26,6 +26,7 @@ import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.realm.Performance
import net.pokeranalytics.android.model.realm.ReportSetup
import net.pokeranalytics.android.calculus.DataManager
import net.pokeranalytics.android.ui.activity.ReportCreationActivity
import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
@ -186,13 +187,13 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
ReportCreationActivity.newInstanceForResult(this, requireContext())
}
this.paApplication?.reportWhistleBlower?.addListener(this)
DataManager.reportWhistleBlower?.addListener(this)
}
override fun onDestroy() {
super.onDestroy()
this.paApplication?.reportWhistleBlower?.removeListener(this)
DataManager.reportWhistleBlower?.removeListener(this)
}
// Rows
@ -265,7 +266,7 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
override fun boolForRow(row: RowRepresentable): Boolean {
val reportRow = row as PerformanceRow
return Preferences.showInAppBadges(requireContext())
&& (this.paApplication?.reportWhistleBlower?.has(reportRow.performance.id) ?: false)
&& (DataManager.reportWhistleBlower?.has(reportRow.performance.id) ?: false)
}
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
@ -321,7 +322,7 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
val report = Calculator.computeStats(realm, options = options)
Timber.d("launchComputation: ${System.currentTimeMillis() - startDate.time}ms")
// Timber.d("launchComputation: ${System.currentTimeMillis() - startDate.time}ms")
launch(Dispatchers.Main) {
if (!isDetached) {
@ -334,7 +335,7 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
}
override fun newBestPerformanceHandler() {
Timber.d("newBestPerformanceHandler called")
// Timber.d("newBestPerformanceHandler called")
activity?.runOnUiThread {
this.dataListAdapter.notifyDataSetChanged()

@ -50,6 +50,7 @@ import net.pokeranalytics.android.util.billing.IAPProducts
import net.pokeranalytics.android.util.billing.PurchaseListener
import net.pokeranalytics.android.util.csv.ProductCSVDescriptors
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import java.io.File
import java.io.IOException
@ -187,17 +188,17 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep
private fun updateMainCurrency(currencyCode: String, rate: Double) {
Preferences.setCurrencyCode(currencyCode, requireContext())
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.where(Currency::class.java).findAll().forEach { currency ->
getRealm().writeAsync { asyncRealm ->
asyncRealm.where(Currency::class.java).findAll().forEach { currency ->
currency.rate = (currency.rate ?: 1.0) * rate
}
realm.where(Session::class.java).findAll().forEach { session ->
asyncRealm.where(Session::class.java).findAll().forEach { session ->
session.bankrollHasBeenUpdated()
}
}
realm.close()
settingsAdapterRow.refreshRow(SettingsRow.CURRENCY)
}

@ -9,7 +9,7 @@ import android.view.*
import androidx.appcompat.widget.Toolbar
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -29,7 +29,6 @@ import net.pokeranalytics.android.ui.modules.filter.FilterActivityRequestCode
import net.pokeranalytics.android.ui.modules.filter.FilterableType
import net.pokeranalytics.android.ui.modules.filter.FiltersActivity
import net.pokeranalytics.android.ui.modules.settings.TransactionFilterActivity
import timber.log.Timber
import java.util.*
class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
@ -75,6 +74,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
addRealmChangeListener(this, UserConfig::class.java)
addRealmChangeListener(this, ComputableResult::class.java)
addRealmChangeListener(this, SessionSet::class.java)
addRealmChangeListener(this, Transaction::class.java)
}
@ -160,9 +160,9 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
CoroutineScope(coroutineContext).launch {
val async = GlobalScope.async {
val async = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>> start...")
// Timber.d(">>> start...")
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -174,7 +174,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in $duration seconds")
// Timber.d(">>> ended in $duration seconds")
}
async.await()
@ -222,7 +222,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
Stat.AVERAGE_BUYIN
)
if (QueryCondition.IsCash.queryWith(realm.where(Result::class.java).isNotNull("tips")).count() > 0) {
if (QueryCondition.IsCash.queryWith(realm.where(Session::class.java).isNotNull("tips")).count() > 0) {
cgStats.add(Stat.TOTAL_TIPS)
}
@ -235,13 +235,13 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
Stat.NUMBER_OF_GAMES,
Stat.AVERAGE_BUYIN
)
if (QueryCondition.IsTournament.queryWith(realm.where(Result::class.java).isNotNull("tips")).count() > 0) {
if (QueryCondition.IsTournament.queryWith(realm.where(Session::class.java).isNotNull("tips")).count() > 0) {
tStats.add(Stat.TOTAL_TIPS)
}
val tSessionGroup = ComputableGroup(Query(QueryCondition.IsTournament).merge(query), tStats)
Timber.d(">>>>> Start computations...")
// Timber.d(">>>>> Start computations...")
val options = Calculator.Options()
val computedStats = mutableListOf<Stat>()

@ -114,7 +114,7 @@ class Top10Fragment : RealmFragment(), RowRepresentableDataSource, RowRepresenta
this.positiveSessions = getRealm().where<Session>()
.isNotNull("startDate")
.isNotNull("endDate")
.greaterThan("result.net", 0.0)
.greaterThan("net", 0.0)
.findAll()
updateTop10()

@ -8,15 +8,16 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.google.android.material.snackbar.Snackbar
import io.realm.RealmObject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.util.extensions.writeAsync
/**
* Deletable Item Fragment
@ -56,7 +57,7 @@ abstract class DeletableItemFragment : RealmFragment() {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
deleteItem(dataListAdapter, deletableItems(), id)
}
@ -88,8 +89,8 @@ abstract class DeletableItemFragment : RealmFragment() {
if (itemToDelete.isValidForDelete(this.getRealm())) {
deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition
getRealm().executeTransaction {
itemToDelete.deleteDependencies(it)
getRealm().writeAsync { asyncRealm ->
itemToDelete.deleteDependencies(asyncRealm)
itemToDelete.deleteFromRealm()
}
itemHasBeenReInserted = false
@ -117,9 +118,9 @@ abstract class DeletableItemFragment : RealmFragment() {
snackBar?.setAction(R.string.cancel) {
if (!itemHasBeenReInserted) {
itemHasBeenReInserted = true
getRealm().executeTransaction { realm ->
getRealm().writeAsync { asyncRealm ->
deletedItem?.let {
val item = realm.copyToRealmOrUpdate(it)
val item = asyncRealm.copyToRealmOrUpdate(it)
updateUIAfterUndoDeletion(item)
}
}

@ -9,8 +9,8 @@ import android.view.*
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -140,7 +140,7 @@ open class FilterableFragment : RealmFragment(), FilterHandler {
viewGroup.removeAllViews()
viewGroup.addView(layoutCurrentFilter)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
viewGroup.visibility = View.VISIBLE
}
@ -153,7 +153,7 @@ open class FilterableFragment : RealmFragment(), FilterHandler {
*/
private fun hideSelectedFilter() {
view?.findViewById<ViewGroup>(R.id.selectedFilter).let {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
it?.visibility = View.GONE
}
}

@ -9,7 +9,6 @@ import io.realm.RealmModel
import io.realm.RealmResults
import kotlinx.coroutines.Dispatchers
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
interface RealmAsyncListener {
@ -70,7 +69,7 @@ open class RealmFragment : BaseFragment() {
this.changeListener = listener
val results = this.realm.where(clazz).findAllAsync()
results.addChangeListener { t, _ ->
Timber.d("Realm changes: ${realmResults?.size}, $this")
// Timber.d("Realm changes: ${realmResults?.size}, $this")
this.changeListener?.asyncListenedEntityChange(t.realm)
}
this.observedRealmResults.add(results)

@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.core.view.get
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import io.realm.RealmObject
import net.pokeranalytics.android.databinding.BottomSheetGameListBinding
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
@ -38,10 +39,11 @@ class BottomSheetListGameFragment : BottomSheetListFragment() {
}
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
this.model.realmData?.let {
val selectedData = it[position]
selectedData?.let { data ->
this.model.someValues[1] = data
this.model.realmData?.let { results ->
val selectedData = results[position]
(selectedData as? RealmObject)?.let { data ->
val obj = results.realm.copyFromRealm(data)
this.model.someValues[1] = obj
this.onRowValueChanged()
// this.delegate.onRowValueChanged(values, this.row)
dismiss()

@ -17,6 +17,7 @@ import net.pokeranalytics.android.ui.modules.data.DataManagerFragment
import net.pokeranalytics.android.ui.viewmodel.ReportViewModel
import net.pokeranalytics.android.ui.viewmodel.ViewModelHolder
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
abstract class AbstractReportFragment : DataManagerFragment() {
@ -84,7 +85,7 @@ abstract class AbstractReportFragment : DataManagerFragment() {
saveReport(nameEditText.text.toString())
dialog.dismiss()
} catch (e: Exception) {
Toast.makeText(requireContext(), e.localizedMessage, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), e.localizedMessage, Toast.LENGTH_LONG).show()
}
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
@ -105,36 +106,34 @@ abstract class AbstractReportFragment : DataManagerFragment() {
}
this.reportViewModel.title = name
val rs = this.model.item as ReportSetup
getRealm().executeTransaction { realm ->
val firstSave = (this.model.primaryKey == null)
if (firstSave) {
val options = this.selectedReport.options
rs.name = name
rs.display = this.reportViewModel.reportDisplay?.ordinal ?: throw PAIllegalStateException("Display not set")
options.stats.forEach {
rs.statIds.add(it.uniqueIdentifier)
}
options.criterias.forEach { criteria ->
when (criteria) {
is CustomFieldCriteria -> rs.criteriaCustomFieldIds.add(criteria.customFieldId)
else -> rs.criteriaIds.add(criteria.uniqueIdentifier)
}
getRealm().writeAsync { asyncRealm ->
val rs = this.model.item as ReportSetup
val options = this.selectedReport.options
rs.name = name
rs.display = this.reportViewModel.reportDisplay?.ordinal
?: throw PAIllegalStateException("Display not set")
rs.statIds.clear()
rs.statIds.addAll(options.stats.map { it.uniqueIdentifier })
rs.criteriaIds.clear()
options.criterias.forEach { criteria ->
when (criteria) {
is CustomFieldCriteria -> rs.criteriaCustomFieldIds.add(criteria.customFieldId)
else -> rs.criteriaIds.add(criteria.uniqueIdentifier)
}
}
options.filterId?.let { id ->
rs.filter = realm.findById(id)
}
realm.copyToRealmOrUpdate(rs)
} else {
rs.name = name
realm.insertOrUpdate(rs)
options.filterId?.let { id ->
rs.filter = asyncRealm.findById(id)
}
asyncRealm.copyToRealmOrUpdate(rs)
this.model.primaryKey = rs.id
}
this.model.primaryKey = rs.id
this.deleteButtonShouldAppear = true
setToolbarTitle(this.reportViewModel.title)
}

@ -8,7 +8,7 @@ import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -30,7 +30,6 @@ import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.StatRow
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.TextFormat
import timber.log.Timber
import java.util.*
open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentableDataSource, CoroutineScope,
@ -203,12 +202,12 @@ open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentab
showLoader()
GlobalScope.launch(coroutineContext) {
CoroutineScope(context = Dispatchers.IO).launch(coroutineContext) {
var report: Report? = null
val test = GlobalScope.async {
val test = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>> start...")
// Timber.d(">>> start...")
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -221,7 +220,7 @@ open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentab
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in $duration seconds")
// Timber.d(">>> ended in $duration seconds")
}
test.await()

@ -11,8 +11,8 @@ import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.AggregationType
@ -30,7 +30,6 @@ import net.pokeranalytics.android.ui.extensions.showWithAnimation
import net.pokeranalytics.android.ui.fragment.GraphFragment
import net.pokeranalytics.android.ui.graph.Graph
import net.pokeranalytics.android.ui.helpers.AppReviewManager
import timber.log.Timber
import java.util.*
@ -163,10 +162,10 @@ class ProgressReportFragment : AbstractReportFragment() {
graphContainer.hideWithAnimation()
progressBar.showWithAnimation()
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
val s = Date()
Timber.d(">>> start...")
// Timber.d(">>> start...")
val realm = Realm.getDefaultInstance()
@ -178,7 +177,7 @@ class ProgressReportFragment : AbstractReportFragment() {
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in $duration seconds")
// Timber.d(">>> ended in $duration seconds")
launch(Dispatchers.Main) {
setGraphData(report, aggregationType)

@ -9,8 +9,8 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.mikephil.charting.data.LineDataSet
import io.realm.RealmResults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -35,8 +35,6 @@ import net.pokeranalytics.android.ui.view.rows.BankrollTotalRow
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
interface BankrollRowRepresentable : RowRepresentable {
var bankrollId: String?
@ -88,7 +86,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
if (requestCode == RequestCode.BANKROLL_DETAILS.value && resultCode == Activity.RESULT_OK) {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
deleteItem(dataListAdapter, bankrolls, id)

@ -13,8 +13,8 @@ import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.tabs.TabLayout
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
@ -167,7 +167,7 @@ class CalendarDetailsFragment : BaseFragment(), StaticRowRepresentableDataSource
this.model.computedResults?.let { computedResults ->
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
val startDate = Date()

@ -11,7 +11,6 @@ import io.realm.Realm
import io.realm.RealmModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
@ -39,7 +38,6 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.util.extensions.*
import timber.log.Timber
import java.util.*
import kotlin.collections.set
@ -348,16 +346,16 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
binding.progressBar.showWithAnimation()
binding.recyclerView.hideWithAnimation()
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
val realm = Realm.getDefaultInstance()
realm.refresh()
// realm.refresh()
launchStatComputation(realm)
realm.close()
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
displayData()
}
}
@ -490,7 +488,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
sortedMonthlyReports = monthlyReports.toSortedMap(compareByDescending { it })
sortedYearlyReports = yearlyReports.toSortedMap(compareByDescending { it })
Timber.d("Computation: ${System.currentTimeMillis() - startDate.time}ms")
// Timber.d("Computation: ${System.currentTimeMillis() - startDate.time}ms")
}
@ -498,7 +496,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
* Display data
*/
private fun displayData() {
Timber.d("displayData")
// Timber.d("displayData")
if (context == null) { return } // required because of launchAsyncStatComputation
@ -594,8 +592,8 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
}
}
Timber.d("Display data: ${System.currentTimeMillis() - startDate.time}ms")
Timber.d("Rows: ${rows.size}")
// Timber.d("Display data: ${System.currentTimeMillis() - startDate.time}ms")
// Timber.d("Rows: ${rows.size}")
this.calendarAdapter.notifyDataSetChanged()

@ -12,7 +12,6 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.*
import java.util.*
import kotlin.collections.HashMap
class GridCalendarViewModel : ViewModel(), RowRepresentableDataSource {
@ -64,8 +63,8 @@ class GridCalendarViewModel : ViewModel(), RowRepresentableDataSource {
while (tmpDate.time <= lastDate.time) {
val result = groupedSessions[tmpDate]?.let { bucket ->
if (bucket.sumByDouble {
it.result?.net ?: 0.0
if (bucket.sumOf {
it?.net ?: 0.0
} > 0.0) CellResult.POSITIVE else CellResult.NEGATIVE
} ?: run {
CellResult.EMPTY

@ -16,6 +16,8 @@ import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.ui.viewmodel.DataManagerViewModel
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
open class DataManagerFragment : RealmFragment() {
@ -96,13 +98,26 @@ open class DataManagerFragment : RealmFragment() {
val status = savable.getSaveValidityStatus(realm = this.getRealm())
when (status) {
SaveValidityStatus.VALID -> {
this.getRealm().executeTransaction {
val managedItem = it.copyToRealmOrUpdate(this.model.item)
if (managedItem is Savable) {
val uniqueIdentifier = managedItem.id
finishActivityWithResult(uniqueIdentifier)
}
val realm = getRealm()
val item = this.model.item
Timber.d("save")
realm.writeAsync { asyncRealm ->
Timber.d("execute async")
asyncRealm.copyToRealmOrUpdate(item)
}
if (item is Savable) {
val uniqueIdentifier = item.id
finishActivityWithResult(uniqueIdentifier)
}
// this.getRealm().executeTransaction {
// val managedItem = it.copyToRealmOrUpdate(this.model.item)
// if (managedItem is Savable) {
// val uniqueIdentifier = managedItem.id
// finishActivityWithResult(uniqueIdentifier)
// }
// }
onDataSaved()
}
else -> {

@ -11,12 +11,13 @@ import io.realm.RealmModel
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowUpdatable
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.extensions.writeAsync
open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegate {
@ -67,16 +68,25 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
}
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
getRealm().executeTransaction {
(this.model.item as RowUpdatable).updateValue(value, row)
this.writeIfPossible(value, row)
rowRepresentableAdapter.refreshRow(row)
}
private fun writeIfPossible(value: Any?, row: RowRepresentable) {
if (!this.isUpdating) {
return
}
getRealm().writeAsync { asyncRealm ->
try {
(this.model.item as RowUpdatable).updateValue(value, row)
asyncRealm.copyToRealmOrUpdate(this.model.item)
} catch (e: Exception) {
CrashLogging.log("Exception caught: row = $row, value=$value, class=${this.javaClass}")
throw e
}
}
rowRepresentableAdapter.refreshRow(row)
}
/**

@ -13,8 +13,8 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -215,7 +215,7 @@ class PlayerDataFragment : EditableDataFragment(), StaticRowRepresentableDataSou
when (row) {
is Comment -> {
if (row.isValidForDelete(getRealm())) {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
showAlertDialog(requireContext(), messageResId = R.string.are_you_sure_you_want_to_delete, showCancelButton = true, positiveAction = {
playerModel.deleteComment(row)

@ -16,6 +16,7 @@ import net.pokeranalytics.android.ui.viewmodel.DataManagerViewModel
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.isSameDay
import net.pokeranalytics.android.util.extensions.mediumDate
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
class PlayerDataViewModel : DataManagerViewModel(), StaticRowRepresentableDataSource {
@ -131,9 +132,11 @@ class PlayerDataViewModel : DataManagerViewModel(), StaticRowRepresentableDataSo
*/
fun cleanupComments() { // called when saving the custom field
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
this.commentsToDelete.forEach { // entries are out of realm
realm.where<Comment>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
val ids = this.commentsToDelete.map { it.id }
realm.writeAsync { asyncRealm ->
ids.forEach { id -> // entries are out of realm
asyncRealm.where<Comment>().equalTo("id", id).findFirst()?.deleteFromRealm()
}
}
realm.close()

@ -2,8 +2,8 @@ package net.pokeranalytics.android.ui.modules.data
import android.content.Context
import io.realm.kotlin.where
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
@ -141,7 +141,7 @@ class TransactionDataFragment : EditableDataFragment(), StaticRowRepresentableDa
when (val next = rows[index + 1]) {
TransactionPropertiesRow.DATE, TransactionPropertiesRow.COMMENT -> {}
else -> {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(200)
onRowSelected(0, next)
}

@ -39,12 +39,13 @@ import net.pokeranalytics.android.ui.modules.filter.FiltersActivity
import net.pokeranalytics.android.ui.modules.handhistory.HandHistoryActivity
import net.pokeranalytics.android.ui.modules.session.SessionActivity
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.URL
import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.billing.PurchaseListener
import net.pokeranalytics.android.util.extensions.count
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseListener {
@ -494,9 +495,14 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis
* Delete selected transaction
*/
private fun deleteSelectedTransaction() {
getRealm().executeTransaction {
selectedTransaction?.deleteFromRealm()
this.selectedTransaction?.id?.let { transactionId ->
getRealm().writeAsync { asyncRealm ->
val transaction = asyncRealm.findById<Transaction>(transactionId)
transaction?.deleteFromRealm()
}
}
selectedTransactionPosition = -1
}

@ -20,9 +20,7 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.SessionRowView
import net.pokeranalytics.android.util.extensions.getMonthAndYear
import timber.log.Timber
import java.util.*
import kotlin.collections.HashMap
/**
@ -182,7 +180,7 @@ class FeedSessionRowRepresentableAdapter(
allSessions.clear()
allSessions.addAll(this.pendingSessions)
allSessions.addAll(this.startedSessions)
Timber.d("Update session list, total count = ${allSessions.size}")
// Timber.d("Update session list, total count = ${allSessions.size}")
val headersPositions = HashMap<Int, Date?>()
@ -210,7 +208,7 @@ class FeedSessionRowRepresentableAdapter(
}
sortedHeaders = headersPositions.toSortedMap()
Timber.d("Create viewTypesPositions in: ${System.currentTimeMillis() - start}ms")
// Timber.d("Create viewTypesPositions in: ${System.currentTimeMillis() - start}ms")
}
/**

@ -9,8 +9,8 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewAnimationUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.databinding.ActivityNewDataBinding
@ -105,7 +105,8 @@ class NewDataMenuActivity : BaseActivity() {
val intent = Intent()
intent.putExtra(IntentKey.CHOICE.keyName, choice)
setResult(RESULT_OK, intent)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(200)
hideMenu()
}

@ -234,17 +234,17 @@ open class FilterDetailsFragment : RealmFragment(), RowRepresentableDelegate {
//TODO: Save currentFilter details data
Timber.d("Save data for queryWith: ${currentFilter?.id}")
this.model.selectedRows.forEach {
Timber.d("Selected rows: $it")
}
// this.model.selectedRows.forEach {
// Timber.d("Selected rows: $it")
// }
this.activityModel.selectedCategoryRow?.let { category ->
getRealm().executeTransaction {
// getRealm().executeTransaction {
currentFilter?.remove(category)
val validConditions = this.model.selectedRows.filter { it.queryCondition != null }
currentFilter?.createOrUpdateFilterConditions(validConditions)
}
// }
}
currentFilter?.filterConditions?.forEach {

@ -4,11 +4,12 @@ import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
import io.realm.Realm
import io.realm.kotlin.where
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import net.pokeranalytics.android.util.enumerations.IntSearchable
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
enum class FilterActivityRequestCode {
SELECT_FILTER,
@ -45,10 +46,9 @@ interface FilterHandler {
fun saveFilter(context: Context, filterId: String) {
Preferences.setActiveFilterId(filterId, context)
val realm = Realm.getDefaultInstance()
realm.executeTransaction { executeRealm ->
currentFilter(context, executeRealm)?.let {
realm.writeAsync { asyncRealm ->
currentFilter(context, asyncRealm)?.let {
it.useCount++
}
}
@ -61,8 +61,9 @@ interface FilterHandler {
}
fun currentFilter(context: Context, realm: Realm): Filter? {
return Preferences.getActiveFilterId(context)?.let {
realm.where<Filter>().equalTo("id", it).findFirst()
return Preferences.getActiveFilterId(context)?.let { id ->
realm.findById(id)
// realm.where<Filter>().equalTo("id", it).findFirst()
} ?: run {
null
}

@ -15,12 +15,12 @@ import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rows.FilterCategoryRow
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.sorted
import net.pokeranalytics.android.util.extensions.writeAsync
open class FiltersFragment : RealmFragment(), RowRepresentableDelegate {
@ -218,16 +218,13 @@ open class FiltersFragment : RealmFragment(), RowRepresentableDelegate {
* Validate the updates of the queryWith
*/
private fun validateUpdates() {
val currentFilter = this.model.currentFilter
getRealm().executeTransaction { realm ->
currentFilter?.let {
it.name = it.query.getName(requireContext())
realm.copyToRealmOrUpdate(it)
}
}
val filterId = currentFilter?.id ?: ""
val filter = this.model.currentFilter
filter?.let { currentFilter ->
getRealm().writeAsync { asyncRealm ->
asyncRealm.copyToRealmOrUpdate(currentFilter)
}
}
val filterId = filter?.id ?: ""
finishActivityWithResult(filterId)
}
@ -239,9 +236,9 @@ open class FiltersFragment : RealmFragment(), RowRepresentableDelegate {
val filterCopy = this.model.filterCopy
val filterId = filterCopy?.id ?: ""
getRealm().executeTransaction { realm ->
getRealm().writeAsync { asyncRealm ->
filterCopy?.let {
realm.copyToRealmOrUpdate(it)
asyncRealm.copyToRealmOrUpdate(it)
}
}
finishActivityWithResult(filterId)

@ -3,11 +3,14 @@ package net.pokeranalytics.android.ui.modules.filter
import android.app.Activity
import android.content.Context
import android.content.Intent
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.modules.datalist.DataListFragment
import net.pokeranalytics.android.ui.modules.filter.FilterHandler.Companion.INTENT_FILTER_UPDATE_FILTER_UI
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
open class FiltersListFragment : DataListFragment() {
@ -15,12 +18,19 @@ open class FiltersListFragment : DataListFragment() {
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
when (row) {
is Filter -> {
getRealm().executeTransaction {
row.updateValue(value, row)
}
val index = this.model.items.indexOf(row)
this.dataListAdapter.notifyItemChanged(index)
updateFilterUIIfNecessary(requireContext(), row.id)
val filterId = row.id
// val filter = row.realm.copyFromRealm(row)
// row.updateValue(value, row)
getRealm().writeAsync ({ asyncRealm ->
asyncRealm.findById<Filter>(filterId)?.let { filter ->
filter.updateValue(value, filter)
// asyncRealm.copyToRealmOrUpdate(filter)
} ?: throw PAIllegalStateException("missing filter: $filterId")
}, {
val index = this.model.items.indexOf(row)
this.dataListAdapter.notifyItemChanged(index)
updateFilterUIIfNecessary(requireContext(), row.id)
} )
}
else -> super.onRowValueChanged(value, row)
}

@ -35,6 +35,7 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
@ -203,7 +204,8 @@ class EditorFragment : RealmFragment(), RowRepresentableDelegate, KeyboardListen
val playerId = data?.getStringExtra(BaseFragment.BundleKey.PRIMARY_KEY.value)
?: throw PAIllegalStateException("Primary key not set where as activity has finished")
getRealm().findById<Player>(playerId)?.let { player ->
this.model.playerSelected(player)
val unmanagedPlayer = player.realm.copyFromRealm(player)
this.model.playerSelected(unmanagedPlayer)
} ?: throw PAIllegalStateException("Player (id=$playerId) not found")
this.editorAdapter.notifyDataSetChanged()
}
@ -684,10 +686,9 @@ class EditorFragment : RealmFragment(), RowRepresentableDelegate, KeyboardListen
*/
private fun deleteHand() {
getRealm().findById<HandHistory>(this.model.handHistory.id)?.let { hh ->
getRealm().executeTransaction {
hh.deleteFromRealm()
}
getRealm().writeAsync { asyncRealm ->
val hh = asyncRealm.findById<HandHistory>(this.model.handHistory.id)
hh?.deleteFromRealm()
}
this.activity?.finish()

@ -5,10 +5,6 @@ import android.text.InputType
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import io.realm.Realm
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.handhistory.HandSetup
@ -27,12 +23,11 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.formatted
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import java.text.DecimalFormat
import java.text.ParseException
import kotlin.coroutines.CoroutineContext
import kotlin.reflect.KClass
enum class HHKeyboard {
@ -49,9 +44,6 @@ interface PlayerSetupCreationListener {
class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer, ActionListListener {
private val coroutineContext: CoroutineContext
get() = Dispatchers.Main
/***
* The hand history
*/
@ -641,33 +633,33 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
* Saves the current hand state in the database
*/
fun save(realm: Realm) {
realm.executeTransaction {
this.handHistory.actions.clear()
val actions = this.sortedActions.map { it.action }
this.handHistory.actions.addAll(actions)
if (!this.handHistory.isManaged) {
realm.copyToRealmOrUpdate(this.handHistory)
}
}
this.defineWinnerPositions()
}
private fun defineWinnerPositions() {
this.handHistory.actions.clear()
val actions = this.sortedActions.map { it.action }
this.handHistory.actions.addAll(actions)
val hhId = this.handHistory.id
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.findById<HandHistory>(hhId)?.defineWinnerPositions()
}
realm.close()
}
c.await()
realm.writeAsync { asyncRealm ->
this.handHistory.defineWinnerPositions()
asyncRealm.copyToRealmOrUpdate(this.handHistory)
}
}
// this.defineWinnerPositions()
}
// private fun defineWinnerPositions() {
//
// val hhId = this.handHistory.id
// GlobalScope.launch(coroutineContext) {
// val c = GlobalScope.async {
// val realm = Realm.getDefaultInstance()
// realm.executeTransaction {
// realm.findById<HandHistory>(hhId)?.defineWinnerPositions()
// }
// realm.close()
// }
// c.await()
// }
//
// }
// Card Centralizer
@ -906,8 +898,8 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
fun changeStraddleSelection(positions: LinkedHashSet<Position>) {
if (positions.isEmpty()) {
this.firstStraddlePosition = null
this.handSetup.clearStraddles()
this.firstStraddlePosition = null
this.handSetup.clearStraddles()
} else {
if (this.firstStraddlePosition == null) {
@ -1013,16 +1005,16 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
fun playerSelected(player: Player) {
// Remove all use of the selected player
this.handHistory.playerSetups.filter { it.player == player }.forEach {
this.handHistory.playerSetups.filter { it.player?.id == player.id }.forEach {
it.player = null
}
// Affects the player to the selected position
this.tappedPlayerPositionIndex?.let { positionIndex ->
player.realm.executeTransaction {
// player.realm.executeTransaction {
val ps = this.handHistory.playerSetupForPosition(positionIndex) ?: this.handHistory.createPlayerSetup(positionIndex)
ps.player = player
}
// }
} ?: throw PAIllegalStateException("Click position not set for player selection")
}
@ -1036,20 +1028,20 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
/***
* Tries to deletes the row at the given [position]
*/
fun deleteIfPossible(position: Int) {
when (val row = this.rowRepresentables[position]) {
is PlayerSetupRow -> {
val playerSetup = this.handHistory.playerSetupForPosition(row.positionIndex) ?: throw PAIllegalStateException("Attempt to delete an null object")
this.handHistory.playerSetups.remove(playerSetup)
this.handHistory.realm?.let {
it.executeTransaction {
playerSetup.deleteFromRealm()
}
}
}
}
}
// fun deleteIfPossible(position: Int) {
// when (val row = this.rowRepresentables[position]) {
// is PlayerSetupRow -> {
// val playerSetup = this.handHistory.playerSetupForPosition(row.positionIndex) ?: throw PAIllegalStateException("Attempt to delete an null object")
// this.handHistory.playerSetups.remove(playerSetup)
//
// this.handHistory.realm?.let {
// it.executeTransaction {
// playerSetup.deleteFromRealm()
// }
// }
// }
// }
// }
fun removePlayerSetup(positionIndex: Int) {
val ps = this.handHistory.playerSetupForPosition(positionIndex)

@ -13,7 +13,10 @@ import android.provider.MediaStore
import androidx.core.content.FileProvider
import com.arthenica.ffmpegkit.FFmpegKit
import io.realm.Realm
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
@ -70,8 +73,9 @@ class ReplayExportService : Service() {
private fun startGIFExport() {
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val c = async {
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -148,8 +152,8 @@ class ReplayExportService : Service() {
val start = Date().time
GlobalScope.launch(coroutineContext) {
val async = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val async = async {
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
@ -287,8 +291,8 @@ class ReplayExportService : Service() {
private fun startGIFExportPreQ() {
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val c = async {
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -349,8 +353,8 @@ class ReplayExportService : Service() {
private fun startFFMPEGVideoExportPreQ() {
GlobalScope.launch(coroutineContext) {
val async = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val async = async {
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")

@ -13,7 +13,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -28,6 +28,7 @@ import net.pokeranalytics.android.model.extensions.getState
import net.pokeranalytics.android.model.extensions.scheduleStopNotification
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.model.utils.FavoriteSessionFinder
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
@ -185,29 +186,31 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
if (sessionRealm != null) {
if (this.model.duplicate) { // duplicate session
realm.executeTransaction {
val session = sessionRealm.duplicate()
currentSession = session
realm.writeAsync { asyncRealm ->
asyncRealm.findById<Session>(sessionId)?.duplicate()?.let { duplicate ->
currentSession = asyncRealm.copyFromRealm(duplicate)
}
// val session = sessionRealm.duplicate()
// currentSession = session
}
sessionHasBeenUserCustomized = false
} else { // show existing session
currentSession = sessionRealm
currentSession = realm.copyFromRealm(sessionRealm)
sessionHasBeenUserCustomized = true
}
} else {
throw PAIllegalStateException("Session cannot be null here, session id = $sessionId")
}
} else { // create new session
realm.executeTransaction { executeRealm ->
currentSession = Session.newInstance(executeRealm, this.model.isTournament)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext())
}
currentSession = Session.newInstance(realm, this.model.isTournament)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext(), realm)
// Find the nearest location around the user
parentActivity?.findNearestLocation {
it?.let { location ->
realm.executeTransaction { executeRealm ->
val realmLocation = executeRealm.findById<Location>(location.id)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext())
realm.writeAsync { asyncRealm ->
val realmLocation = asyncRealm.findById<Location>(location.id)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext(), asyncRealm)
currentSession.location = realmLocation
}
@ -217,6 +220,10 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
sessionHasBeenUserCustomized = false
}
// load everything and have some SessionManager links it all
//
}
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
@ -249,7 +256,8 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
showBottomSheet(row, this, data, false, session.currency)
}
SessionPropertiesRow.HANDS -> {
val hhIds = session.handHistories?.map { it.id }?.toTypedArray()
val handHistories = getRealm().where(HandHistory::class.java).equalTo("session.id", session.id).findAll()
val hhIds = handHistories?.map { it.id }?.toTypedArray()
DataListActivity.newInstance(this, LiveData.HAND_HISTORY, false, hhIds, false)
}
else -> showBottomSheet(row, this, data, currentCurrency = session.currency)
@ -259,9 +267,8 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
this.sessionHasBeenUserCustomized = true
try {
getRealm().executeTransaction {
this.currentSession.updateValue(value, row)
}
this.currentSession.updateValue(value, row)
this.writeChanges()
} catch (e: PAIllegalStateException) {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
return
@ -279,6 +286,25 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
}
private fun writeChanges() {
Timber.d("write changes")
getRealm().writeAsync ({ asyncRealm ->
Timber.d("start transaction")
asyncRealm.copyToRealmOrUpdate(this.currentSession)
Timber.d("yupyup")
}, { // onSuccess we retrieved the object because it might have been changed (i.e. session set added). Not retrieving it can for example copy it without its sessionset, writing null in db whereas it should have it
Timber.d("onSuccess")
val id = this.currentSession.id
getRealm().refresh()
getRealm().findById<Session>(id)?.let { session ->
this.currentSession = getRealm().copyFromRealm(session)
Timber.d("Session retrieved, set = ${this.currentSession.sessionSet}, br = ${session.bankroll}")
} ?: throw PAIllegalStateException("session not found")
})
}
/**
* Update the UI with the session data
* Should be called after the initialization of the session
@ -393,11 +419,14 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
}
currentSession.startOrContinue()
this.writeChanges()
binding.recyclerView.smoothScrollToPosition(0)
}
SessionState.STARTED -> {
currentSession.pause()
this.writeChanges()
}
else -> {
}
@ -405,6 +434,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
updateSessionUI()
}
private fun computeOptimalDuration() {
Timber.d("Start optimal duration finding attempt...")
@ -414,7 +444,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
var optimalDuration: Double? = null
val cr = GlobalScope.async {
val cr = CoroutineScope(context = Dispatchers.IO).async {
optimalDuration = CashGameOptimalDurationCalculator.start(isLive)
}
cr.await()
@ -426,7 +456,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
currentSession.scheduleStopNotification(requireContext(), delay)
val formattedDuration = (it / 3600 / 1000).formattedHourlyDuration()
Timber.d("Setting stop notification in: $formattedDuration")
// Timber.d("Setting stop notification in: $formattedDuration")
val message = requireContext().getString(R.string.stop_notification_in_, formattedDuration)
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
}
@ -482,7 +512,17 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
val bankrollId = this.currentSession.bankroll?.id
this.currentSession.delete()
val id = currentSession.id
getRealm().writeAsync ({ asyncRealm ->
asyncRealm.findById<Session>(id)?.let { session ->
session.cleanup()
session.deleteFromRealm()
} ?: throw PAIllegalStateException("session not found")
}, {
Timber.d("delete successful")
// getRealm().refresh()
})
bankrollId?.let {
BankrollReportManager.notifyBankrollReportImpact(bankrollId)
}
@ -495,7 +535,11 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
override fun onBackPressed() {
super.onBackPressed()
if (!sessionHasBeenUserCustomized) {
currentSession.delete()
val id = currentSession.id
getRealm().writeAsync { asyncRealm ->
asyncRealm.findById<Session>(id)?.delete()
}
}
}
@ -526,7 +570,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
}
override fun charSequenceForRow(row: RowRepresentable, context: Context): String {
return this.currentSession.charSequenceForRow(row, context)
return this.currentSession.charSequenceForRow(row, context, getRealm())
}
override fun actionIconForRow(row: RowRepresentable): Int? {
@ -593,18 +637,18 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
mapOf(
"bb" to session.cgBiggestBet,
"fee" to session.tournamentEntryFee,
"ratedBuyin" to session.result?.buyin
"ratedBuyin" to session.buyin
)
)
SessionPropertiesRow.BREAK_TIME -> row.editingDescriptors(mapOf())
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> row.editingDescriptors(
mapOf(
"defaultValue" to session.result?.cashout
"defaultValue" to session.cashout
)
)
SessionPropertiesRow.NET_RESULT -> row.editingDescriptors(
mapOf(
"defaultValue" to session.result?.netResult
"defaultValue" to session.netResult
)
)
SessionPropertiesRow.COMMENT -> row.editingDescriptors(
@ -624,7 +668,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
)
SessionPropertiesRow.POSITION -> row.editingDescriptors(
mapOf(
"defaultValue" to session.result?.tournamentFinalPosition
"defaultValue" to session.tournamentFinalPosition
)
)
SessionPropertiesRow.HANDS_COUNT -> row.editingDescriptors(
@ -641,7 +685,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
mapOf(
"sb" to session.cgBiggestBet?.round(),
"bb" to session.cgBiggestBet?.round(),
"tips" to session.result?.tips
"tips" to session.tips
)
)
is CustomField -> {
@ -663,16 +707,15 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
override fun resultCaptureTypeSelected(resultCaptureType: ResultCaptureType, applyBankroll: Boolean) {
getRealm().executeTransaction { // cleanup existing results
when (resultCaptureType) {
ResultCaptureType.NET_RESULT -> {
this.currentSession.clearBuyinCashedOut()
}
ResultCaptureType.BUYIN_CASHED_OUT -> {
this.currentSession.clearNetResult()
}
when (resultCaptureType) {
ResultCaptureType.NET_RESULT -> {
this.currentSession.clearBuyinCashedOut()
}
ResultCaptureType.BUYIN_CASHED_OUT -> {
this.currentSession.clearNetResult()
}
}
this.writeChanges()
this.model.resultCaptureType = resultCaptureType
if (applyBankroll) {

@ -17,7 +17,6 @@ import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.SeparatorRow
import net.pokeranalytics.android.ui.view.rows.SessionPropertiesRow
import net.pokeranalytics.android.util.extensions.sorted
import java.util.ArrayList
class SessionViewModel : ViewModel() {
@ -52,7 +51,7 @@ class SessionViewModel : ViewModel() {
fun updatedRowRepresentationForCurrentState(session: Session, realm: Realm, context: Context) {
val rows = ArrayList<RowRepresentable>()
val result = session.result
// val result = session.result
val currency = session.currency
// Headers
@ -62,7 +61,7 @@ class SessionViewModel : ViewModel() {
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = session.getFormattedDuration(),
valueTextFormat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = session.currency).textFormat
valueTextFormat = ComputedStat(Stat.NET_RESULT, session.net, currency = session.currency).textFormat
)
)
rows.add(SeparatorRow())
@ -72,7 +71,7 @@ class SessionViewModel : ViewModel() {
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
resId = R.string.pause,
valueTextFormat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency).textFormat
valueTextFormat = ComputedStat(Stat.NET_RESULT, session.net, currency = currency).textFormat
)
)
rows.add(SeparatorRow())
@ -82,7 +81,7 @@ class SessionViewModel : ViewModel() {
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = session.getFormattedDuration(),
valueTextFormat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency).textFormat
valueTextFormat = ComputedStat(Stat.NET_RESULT, session.net, currency = currency).textFormat
)
)
rows.add(
@ -100,11 +99,14 @@ class SessionViewModel : ViewModel() {
}
// Rows
rows.addAll(SessionPropertiesRow.getRows(session, this.resultCaptureType, context))
rows.addAll(SessionPropertiesRow.getRows(session, this.resultCaptureType, context, realm))
// Add custom fields
rows.add(SeparatorRow())
rows.addAll(realm.sorted<CustomField>())
val customFields = realm.sorted<CustomField>()
val unmanaged = customFields.map { realm.copyFromRealm(it) }
rows.addAll(unmanaged)
this.rows = rows
}

@ -10,6 +10,7 @@ import net.pokeranalytics.android.databinding.FragmentDealtHandsConfigBinding
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.UserConfig
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.util.extensions.writeAsync
class DealtHandsPerHourFragment : RealmFragment() {
@ -57,19 +58,19 @@ class DealtHandsPerHourFragment : RealmFragment() {
private fun save() {
getRealm().executeTransaction { realm ->
getRealm().writeAsync { asyncRealm ->
val userConfig = UserConfig.getConfiguration(realm)
val userConfig = UserConfig.getConfiguration(asyncRealm)
this.liveValue.text.toString().toIntOrNull()?.let { liveDealtHandsPerHour ->
userConfig.liveDealtHandsPerHour = liveDealtHandsPerHour
}
this.onlineValue.text.toString().toIntOrNull()?.let { onlineDealtHandsPerHour ->
userConfig.onlineDealtHandsPerHour = onlineDealtHandsPerHour
}
realm.copyToRealmOrUpdate(userConfig)
asyncRealm.copyToRealmOrUpdate(userConfig)
// update all precomputed hand counts
realm.where(ComputableResult::class.java).findAll().forEach { cr ->
asyncRealm.where(ComputableResult::class.java).findAll().forEach { cr ->
cr.session?.let { session ->
cr.estimatedHands = session.estimatedHands
}

@ -16,6 +16,7 @@ import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.writeAsync
class TransactionFilterFragment : RealmFragment(), StaticRowRepresentableDataSource,
RowRepresentableDelegate {
@ -96,10 +97,13 @@ class TransactionFilterFragment : RealmFragment(), StaticRowRepresentableDataSou
}
private fun save() {
getRealm().executeTransaction { realm ->
val userConfig = UserConfig.getConfiguration(realm)
userConfig.setTransactionTypeIds(this.model.selectedTransactionTypes)
realm.copyToRealmOrUpdate(userConfig)
val ids = this.model.selectedTransactionTypes.map { it.id }
// this.model.selectedTransactionTypes.joinToString(UUID_SEPARATOR) { it.id }
getRealm().writeAsync { asyncRealm ->
val userConfig = UserConfig.getConfiguration(asyncRealm)
userConfig.setTransactionTypeIds(ids.toSet())
asyncRealm.copyToRealmOrUpdate(userConfig)
}
this.activity?.finish()
}

@ -511,9 +511,8 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
if (row is Session) {
itemView.findViewById<AppCompatTextView>(R.id.gameResult)?.let { gameResult ->
val result = row.result?.net ?: 0.0
val formattedStat =
ComputedStat(Stat.NET_RESULT, result, currency = row.currency).textFormat
ComputedStat(Stat.NET_RESULT, row.net, currency = row.currency).textFormat
gameResult.setTextFormat(formattedStat, itemView.context)
}

@ -147,10 +147,8 @@ class SessionRowView : FrameLayout {
this.infoIcon.isVisible = false
this.infoTitle.isVisible = false
session.result?.net?.let { netResult ->
val stat = ComputedStat(Stat.NET_RESULT, netResult, currency = session.currency)
this.gameResult.setTextFormat(stat.textFormat, context)
}
val stat = ComputedStat(Stat.NET_RESULT, session.net, currency = session.currency)
this.gameResult.setTextFormat(stat.textFormat, context)
// val formattedStat = ComputedStat(Stat.NET_RESULT, result, currency = session.currency).format()
}

@ -2,6 +2,7 @@ package net.pokeranalytics.android.ui.view.rows
import android.content.Context
import android.text.InputType
import io.realm.Realm
import io.realm.RealmResults
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.TournamentType
@ -9,6 +10,7 @@ import net.pokeranalytics.android.model.extensions.SessionState
import net.pokeranalytics.android.model.extensions.getState
import net.pokeranalytics.android.model.realm.ResultCaptureType
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
@ -48,7 +50,7 @@ enum class SessionPropertiesRow : RowRepresentable {
/**
* Return the rows to display for the current session state
*/
fun getRows(session: Session, resultCaptureType: ResultCaptureType?, context: Context): List<RowRepresentable> {
fun getRows(session: Session, resultCaptureType: ResultCaptureType?, context: Context, realm: Realm): List<RowRepresentable> {
val state = session.getState()
when (session.type) {
Session.Type.TOURNAMENT.ordinal -> {
@ -77,6 +79,8 @@ enum class SessionPropertiesRow : RowRepresentable {
TIPS))
fields.add(SeparatorRow())
fields.add(COMMENT)
if (session.handHistories?.isNotEmpty() == true) {
fields.add(HANDS)
}
@ -133,7 +137,9 @@ enum class SessionPropertiesRow : RowRepresentable {
// PROPERTIES
fields.add(SeparatorRow())
fields.add(COMMENT)
if (session.handHistories?.isNotEmpty() == true) {
val handHistories = realm.where(HandHistory::class.java).equalTo("session.id", session.id).findAll()
if (handHistories?.isNotEmpty() == true) {
fields.add(HANDS)
}
fields.add(SeparatorRow())

@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException
@ -211,17 +212,32 @@ class BottomSheetViewModel(var row: RowRepresentable) : ViewModel() {
}
return null
}
BottomSheetType.MULTI_SELECTION -> this.selectedRows
BottomSheetType.MULTI_SELECTION -> this.selectedRowsRealmCopy()
BottomSheetType.NUMERIC_TEXT -> this.doubleValue
BottomSheetType.GRID -> this.defaultSize
BottomSheetType.DOUBLE_LIST, BottomSheetType.LIST_GAME -> this.someValues
BottomSheetType.LIST_STATIC -> this.selectedRows.firstOrNull()
BottomSheetType.LIST_STATIC -> this.selectedRowsRealmCopy().firstOrNull()
BottomSheetType.SUM -> this.doubleValue
BottomSheetType.CASH_GAME_STAKES -> Stakes(this.secondStringValue, this.ante)
else -> null
}
}
private fun selectedRowsRealmCopy(): List<*> {
val realmObjects = this.selectedRows.filterIsInstance<RealmObject>()
return if (realmObjects.isNotEmpty()) {
realmObjects.map { obj ->
if (obj.isManaged) {
obj.realm.copyFromRealm(obj)
} else {
obj
}
}
} else {
this.selectedRows
}
}
fun isSelected(row: RowRepresentable): Boolean {
return this.selectedRows.contains(row)
}
@ -243,9 +259,17 @@ class BottomSheetViewModel(var row: RowRepresentable) : ViewModel() {
}
fun rowSelected(position: Int): RowRepresentable? {
return when(this.row.bottomSheetType) {
BottomSheetType.LIST -> this.realmData?.get(position)
BottomSheetType.LIST_STATIC -> this.staticRows[position]
when(this.row.bottomSheetType) {
BottomSheetType.LIST -> {
this.realmData?.realm?.let { realm ->
val item = this.realmData?.get(position) as? RealmObject
item?.let {
return realm.copyFromRealm(it) as? RowRepresentable
}
}
return null
}
BottomSheetType.LIST_STATIC -> return this.staticRows[position]
else -> throw PAIllegalStateException("row selected for unmanaged bottom sheet type")
}
}

@ -40,7 +40,7 @@ class BackupOperator(var context: Context) {
fun backupIfNecessary() {
Timber.d(">>> backupIfNecessary")
// Timber.d(">>> backupIfNecessary")
Preferences.getBackupEmail(context)?.let { email ->
this.backupSessionsIfNecessary(email)

@ -34,14 +34,6 @@ class FakeDataManager {
val bankroll = realm.where<Bankroll>().findAll().firstOrNull()
val locations = realm.where<Location>().findAll()
if (locations.size == 0) {
realm.executeTransaction {
listOf("Bellagio", "Aria", "Borgata").map {
realm.getOrCreate<Location>(it)
}
}
}
// Test endedSessions
Timber.d("*** Start creating ${numberOfSessions} fake computables...")
@ -50,10 +42,16 @@ class FakeDataManager {
realm.beginTransaction()
if (locations.size == 0) {
listOf("Bellagio", "Aria", "Borgata").map {
realm.getOrCreate<Location>(it)
}
}
for (index in 1..numberOfSessions) {
if (index % commitFrequency == 0) {
Timber.d("****** committing at ${index} computables...")
Timber.d("****** committing at ${index} sessions...")
realm.commitTransaction()
realm.beginTransaction()
}
@ -82,10 +80,8 @@ class FakeDataManager {
session.tableSize = (2..10).random()
val buyin = buyinList.random()
session.result?.let { result ->
result.buyin = buyinList.random()
result.cashout = resultsList.random() + buyin
}
session.buyin = buyinList.random()
session.cashout = resultsList.random() + buyin
if (isTournament) {
session.tournamentEntryFee = buyin
@ -99,6 +95,8 @@ class FakeDataManager {
// session.cgSmallBlind = bigBlind / 2.0
}
realm.copyToRealmOrUpdate(session)
}
realm.commitTransaction()

@ -7,6 +7,7 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.ObjectIdentifier
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import org.apache.commons.csv.CSVRecord
/**
@ -70,8 +71,8 @@ abstract class DataCSVDescriptor<T : Identifiable>(source: DataSource, vararg el
if (realm.isInTransaction) {
this.deleteInsertedFromRealm(realm)
} else {
realm.executeTransaction {
this.deleteInsertedFromRealm(realm)
realm.writeAsync { asyncRealm ->
this.deleteInsertedFromRealm(asyncRealm)
}
}

@ -26,7 +26,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
vararg elements: CSVField)
: DataCSVDescriptor<T>(source, *elements) {
var noSessionImport: Boolean = false
private var noSessionImport: Boolean = false
init {
val realm = Realm.getDefaultInstance()
@ -46,7 +46,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
protected fun parseSession(realm: Realm, record: CSVRecord, context: Context): Session? {
val isTournament = isTournament ?: false
val session = Session.newInstance(realm, isTournament, managed = false)
val session = Session.newInstance(realm, isTournament)
var startDate: Date? = null
var endDate: Date? = null
@ -127,13 +127,13 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
}
is SessionField.Buyin -> {
val buyin = field.parse(value)
session.result?.buyin = buyin
session.buyin = buyin
if (session.type == Session.Type.TOURNAMENT.ordinal) {
session.tournamentEntryFee = buyin
} else {}
}
is SessionField.CashedOut -> session.result?.cashout = field.parse(value)
is SessionField.NetResult -> session.result?.netResult = field.parse(value)
is SessionField.CashedOut -> session.cashout = field.parse(value)
is SessionField.NetResult -> session.netResult = field.parse(value)
is SessionField.SessionType -> {
Session.Type.getValueFromString(value)?.let { type ->
session.type = type.ordinal
@ -143,7 +143,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
is SessionField.NumberOfTables -> session.numberOfTables = field.parse(value) ?: 1
is SessionField.Addon -> additionalBuyins += field.parse(value) ?: 0.0
is SessionField.Rebuy -> additionalBuyins += field.parse(value) ?: 0.0
is SessionField.Tips -> session.result?.tips = field.parse(value)
is SessionField.Tips -> session.tips = field.parse(value)
is SessionField.HandsCount -> session.handsCount = field.parse(value)
is SessionField.Break -> {
field.parse(value)?.let {
@ -191,7 +191,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
session.cgAnte = field.parse(value)
}
is SessionField.TableSize -> session.tableSize = TableSize.valueForLabel(value)
is SessionField.TournamentPosition -> session.result?.tournamentFinalPosition =
is SessionField.TournamentPosition -> session.tournamentFinalPosition =
field.parse(value)
is SessionField.TournamentName -> {
if (value.isNotEmpty()) {
@ -232,6 +232,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
Timber.d("N>> create: $number")
val entry = realm.copyToRealm(CustomFieldEntry())
entry.numericValue = number
entry.customField = customField
customField.entries.add(entry)
session.customFieldEntries.add(entry)
@ -255,12 +256,12 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
val bankroll = Bankroll.getOrCreate(realm, bankrollName, isLive, currencyCode, currencyRate)
session.bankroll = bankroll
session.result?.buyin?.let {
session.result?.buyin = it + additionalBuyins
session.buyin?.let {
session.buyin = it + additionalBuyins
}
val net = session.result?.net
val net = session.net
if (startDate != null && net != null) { // valid session
if (startDate != null) { // valid session
// session already in realm, we'd love not put it in Realm before doing the check
val count = DataUtils.sessionCount(realm, startDate!!, endDate, net)
if (this.noSessionImport || count == 0) {

@ -46,10 +46,10 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
is SessionField.SessionType -> Session.Type.values()[data.type].value
is SessionField.Live -> field.format(data.isLive)
is SessionField.NumberOfTables -> field.format(data.numberOfTables)
is SessionField.Buyin -> field.format(data.result?.buyin)
is SessionField.CashedOut -> field.format(data.result?.cashout)
is SessionField.NetResult -> field.format(data.result?.netResult)
is SessionField.Tips -> field.format(data.result?.tips)
is SessionField.Buyin -> field.format(data.buyin)
is SessionField.CashedOut -> field.format(data.cashout)
is SessionField.NetResult -> field.format(data.netResult)
is SessionField.Tips -> field.format(data.tips)
is SessionField.HandsCount -> field.format(data.handsCount)
is SessionField.LimitType -> {
data.limit?.let { limit ->
@ -76,7 +76,7 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
is SessionField.TournamentFeatures -> field.format(data.tournamentFeatures)
is SessionField.TournamentEntryFee -> field.format(data.tournamentEntryFee)
is SessionField.TournamentNumberOfPlayers -> field.format(data.tournamentNumberOfPlayers)
is SessionField.TournamentPosition -> field.format(data.result?.tournamentFinalPosition)
is SessionField.TournamentPosition -> field.format(data.tournamentFinalPosition)
is SessionField.Comment -> data.comment
is SessionField.NumberCustomField -> {
val entry = data.customFieldEntries.find { it.customField?.id == field.customField.id }

@ -7,6 +7,8 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.UsageCountable
import net.pokeranalytics.android.model.realm.*
import timber.log.Timber
import java.util.*
fun <T : RealmModel>Realm.count(clazz: Class<T>) : Long {
return this.where(clazz).count()
@ -91,13 +93,52 @@ inline fun <reified C : RealmModel> Realm.sorted(editableOnly: Boolean = false,
return this.sorted(C::class.java, editableOnly, omitId = omitId)
}
fun Realm.writeAsync(handler: (Realm) -> (Unit)) {
Timber.d("Start write...")
this.executeTransactionAsync { asyncRealm ->
handler(asyncRealm)
}
// this.executeTransactionAsync({ asyncRealm ->
// handler(asyncRealm)
// }, { // success
//
// }, { error -> // error
// Timber.w("Realm write error: $error")
// })
}
fun Realm.writeAsync(handler: (Realm) -> (Unit), success: () -> (Unit)) {
Timber.d("Start write with success/error...")
val s = Date()
this.executeTransactionAsync({ asyncRealm ->
handler(asyncRealm)
Timber.d("REALM execution ended")
}, { // success
val e = Date()
val duration = e.time - s.time
Timber.d("//// transaction duration = $duration")
Timber.d("SUCCESS!")
success()
}, { error -> // error
Timber.d("Realm write error: $error")
})
}
/**
* Updates the useCount variable of the CountableUsage entity
*/
fun <T : RealmModel>Realm.updateUsageCount(clazz: Class<T>) {
val results = this.where(clazz).findAll()
this.executeTransaction {
this.writeAsync { asyncRealm ->
val results = asyncRealm.where(clazz).findAll()
results.forEach { countableUsage ->
val countable = (countableUsage as UsageCountable)
@ -106,7 +147,7 @@ fun <T : RealmModel>Realm.updateUsageCount(clazz: Class<T>) {
TournamentFeature::class -> "tournamentFeatures.id"
else -> "${clazz.simpleName.decapitalize()}.id"
}
val count = it.where(countable.ownerClass).contains(fieldName, countable.id).count().toInt()
val count = asyncRealm.where(countable.ownerClass).contains(fieldName, countable.id).count().toInt()
countable.useCount = count
}
}

@ -25,7 +25,6 @@
android:id="@+id/newTransaction"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
@ -103,7 +102,6 @@
android:id="@+id/newCashGame"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"

Loading…
Cancel
Save