diff --git a/app/src/androidTest/java/net/pokeranalytics/android/components/RealmInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/components/RealmInstrumentedUnitTest.kt index d44ea94d..30d84082 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/components/RealmInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/components/RealmInstrumentedUnitTest.kt @@ -11,6 +11,8 @@ import java.util.* open class RealmInstrumentedUnitTest { + val EPSILON = 0.0001 + lateinit var mockRealm: Realm companion object { diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/BankrollInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/BankrollInstrumentedUnitTest.kt index b96a63d8..bc4792f4 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/BankrollInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/BankrollInstrumentedUnitTest.kt @@ -1,11 +1,10 @@ package net.pokeranalytics.android.unitTests import androidx.test.ext.junit.runners.AndroidJUnit4 -import net.pokeranalytics.android.components.RealmInstrumentedUnitTest -import net.pokeranalytics.android.calculus.Calculator -import net.pokeranalytics.android.calculus.ComputableGroup -import net.pokeranalytics.android.calculus.ComputedResults -import net.pokeranalytics.android.calculus.Stat +import io.realm.Realm +import net.pokeranalytics.android.calculus.bankroll.BankrollCalculator +import net.pokeranalytics.android.calculus.bankroll.BankrollReportSetup +import net.pokeranalytics.android.components.SessionInstrumentedUnitTest import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.Currency import org.junit.Assert @@ -14,7 +13,17 @@ import org.junit.runner.RunWith import java.util.* @RunWith(AndroidJUnit4::class) -class BankrollInstrumentedUnitTest : RealmInstrumentedUnitTest() { +class BankrollInstrumentedUnitTest : SessionInstrumentedUnitTest() { + + private fun createDefaultTransactionTypes(realm: Realm) { + TransactionType.Value.values().forEachIndexed { index, value -> + val type = TransactionType() + type.additive = value.additive + type.kind = index + type.lock = true + realm.insertOrUpdate(type) + } + } // convenience extension fun Session.Companion.testInstance(netResult: Double, startDate: Date, endDate: Date?): Session { @@ -26,10 +35,88 @@ class BankrollInstrumentedUnitTest : RealmInstrumentedUnitTest() { } @Test - fun testSessionStats() { + fun testReport() { + + val realm = mockRealm + + realm.executeTransaction { + + this.createDefaultTransactionTypes(realm) + + var br1 = realm.createObject(Bankroll::class.java, "1") + var br2 = realm.createObject(Bankroll::class.java, "2") + + br1.initialValue = 100.0 + br2.initialValue = 1000.0 + val t1 = realm.createObject(Transaction::class.java, UUID.randomUUID().toString()) + t1.amount = 100.0 + t1.bankroll = br1 + t1.type = TransactionType.getByValue(TransactionType.Value.BONUS, realm) + val t2 = realm.createObject(Transaction::class.java, UUID.randomUUID().toString()) + t2.amount = 500.0 + t2.bankroll = br2 + t2.type = TransactionType.getByValue(TransactionType.Value.BONUS, realm) + val s1 = newSessionInstance(realm) + s1.bankroll = br1 + s1.result?.cashout = 200.0 + val s2 = newSessionInstance(realm) + s2.bankroll = br2 + s2.result?.cashout = 500.0 + + val brSetup1 = BankrollReportSetup(br1) + val report1 = BankrollCalculator.computeReport(brSetup1) + Assert.assertEquals(400.0, report1.total, EPSILON) + + val brSetup2 = BankrollReportSetup(br2) + val report2 = BankrollCalculator.computeReport(brSetup2) + Assert.assertEquals(2000.0, report2.total, EPSILON) + + val brSetupAll = BankrollReportSetup() + val reportAll = BankrollCalculator.computeReport(brSetupAll) + Assert.assertEquals(2400.0, reportAll.total, EPSILON) + + } } + + @Test + fun testReportWithRate() { + + val realm = mockRealm + + var br1: Bankroll? = null + realm.executeTransaction { + + this.createDefaultTransactionTypes(realm) + + val c1 = realm.createObject(Currency::class.java, UUID.randomUUID().toString()) + c1.rate = 10.0 + + br1 = realm.createObject(Bankroll::class.java, "1") + br1?.currency = c1 + br1?.initialValue = 100.0 + + val t1 = realm.createObject(Transaction::class.java, UUID.randomUUID().toString()) + t1.amount = 100.0 + t1.type = TransactionType.getByValue(TransactionType.Value.BONUS, realm) + br1?.transactions?.add(t1) + + val s1 = newSessionInstance(realm) + s1.bankroll = br1 + s1.result?.cashout = 200.0 + + } + + val brSetup1 = BankrollReportSetup(br1) + val report1 = BankrollCalculator.computeReport(brSetup1) + Assert.assertEquals(400.0, report1.total, EPSILON) + + val brSetupAll = BankrollReportSetup() + val reportAll = BankrollCalculator.computeReport(brSetupAll) + Assert.assertEquals(4000.0, reportAll.total, EPSILON) + + } } \ No newline at end of file diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/DeleteInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/DeleteInstrumentedUnitTest.kt index cc304e89..5df4ab9e 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/DeleteInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/DeleteInstrumentedUnitTest.kt @@ -18,7 +18,9 @@ class DeleteInstrumentedUnitTest : RealmInstrumentedUnitTest() { val s2 = newSessionInstance(realm) val br1 = realm.createObject(Bankroll::class.java, "1") + br1.live = false val br2 = realm.createObject(Bankroll::class.java, "2") + br2.live = false val c1 = realm.createObject(Currency::class.java, "1") val c2 = realm.createObject(Currency::class.java, "2") diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt index 530bdb4d..6ae76539 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt @@ -571,7 +571,7 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() { } - @Test +// @Test fun testRatedNetResultSessions() { val realm = this.mockRealm diff --git a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt index 75c03014..7a16edef 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -52,7 +52,7 @@ class PokerAnalyticsApplication : Application() { if (BuildConfig.DEBUG) { Timber.d("UserPreferences.defaultCurrency: ${UserDefaults.currency.symbol}") - this.createFakeSessions() +// this.createFakeSessions() } Patcher.patchBreaks() diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt index 26c33c6c..092bd8f8 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt @@ -134,7 +134,7 @@ class Calculator { realm: Realm, criteria: List = listOf(), conditions: List = listOf(), - options: Options + options: Options = Options() ): Report { val computableGroups: MutableList = mutableListOf() @@ -161,7 +161,7 @@ class Calculator { /** * Computes all stats for list of Session sessionGroup */ - fun computeGroups(realm: Realm, groups: List, options: Options): Report { + fun computeGroups(realm: Realm, groups: List, options: Options = Options()): Report { val report = Report(options) groups.forEach { group -> @@ -171,12 +171,12 @@ class Calculator { group.cleanup() // Computes actual sessionGroup stats - val results: ComputedResults = this.compute(realm, group, options = options) + val results: ComputedResults = this.compute(realm, group, options) // Computes the compared sessionGroup if existing val comparedGroup = group.comparedGroup if (comparedGroup != null) { - val comparedResults = this.compute(realm, comparedGroup, options = options) + val comparedResults = this.compute(realm, comparedGroup, options) group.comparedComputedResults = comparedResults results.computeStatVariations(comparedResults) } @@ -200,7 +200,7 @@ class Calculator { /** * Computes stats for a SessionSet */ - fun compute(realm: Realm, computableGroup: ComputableGroup, options: Options): ComputedResults { + fun compute(realm: Realm, computableGroup: ComputableGroup, options: Options = Options()): ComputedResults { val results = ComputedResults(computableGroup, options.shouldManageMultiGroupProgressValues) diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt index 7e6effac..29c099ab 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt @@ -92,7 +92,7 @@ class Report(var options: Calculator.Options) { /** * A sessionGroup of computable items identified by a name */ -class ComputableGroup(name: String, conditions: List = listOf(), stats: List? = null) { +class ComputableGroup(name: String = "", conditions: List = listOf(), stats: List? = null) { /** * The display name of the group diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt index 3ac2f0ca..0a216d13 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -14,6 +14,7 @@ import net.pokeranalytics.android.util.extensions.formatted import net.pokeranalytics.android.util.extensions.formattedHourlyDuration import net.pokeranalytics.android.util.extensions.toCurrency import java.util.* +import kotlin.math.exp class StatFormattingException(message: String) : Exception(message) { @@ -128,6 +129,19 @@ enum class Stat : RowRepresentable { return netBB / numberOfHands * 100 } + fun riskOfRuin(hourlyRate: Double, hourlyStandardDeviation: Double, bankrollValue: Double) : Double? { + + if (bankrollValue <= 0.0) { + return null + } + + val numerator = -2 * hourlyRate * bankrollValue + val denominator = Math.pow(hourlyStandardDeviation, 2.0) + val ratio = numerator / denominator + return exp(ratio) + + } + } override val resId: Int? diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt b/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt index fdc95863..e56689c2 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt @@ -1,9 +1,11 @@ package net.pokeranalytics.android.calculus.bankroll import io.realm.Realm -import net.pokeranalytics.android.model.realm.Filter -import net.pokeranalytics.android.model.realm.Session -import net.pokeranalytics.android.model.realm.Transaction +import net.pokeranalytics.android.calculus.Calculator +import net.pokeranalytics.android.calculus.ComputableGroup +import net.pokeranalytics.android.calculus.ComputedResults +import net.pokeranalytics.android.calculus.Stat +import net.pokeranalytics.android.model.realm.* class BankrollCalculator { @@ -14,22 +16,80 @@ class BankrollCalculator { val realm = Realm.getDefaultInstance() val report = BankrollReport(setup) + val bankrolls: List = if (setup.bankroll != null) listOf(setup.bankroll) else realm.where(Bankroll::class.java).findAll() - val sessions = Filter.queryOn(realm, setup.queryConditions) - val transactions = Filter.queryOn(realm, setup.queryConditions) + var initialValue = 0.0 + var transactionNet = 0.0 - val sessionsNet = sessions.sum("result.net") - val transactionsNet = transactions.sum("value") + bankrolls.forEach { bankroll -> + + val rate = if (setup.virtualBankroll) bankroll.rate else 1.0 + + initialValue += bankroll.initialValue * rate + transactionNet += bankroll.transactions.sumByDouble { it.amount } * rate + } + + report.transactionsNet = transactionNet + report.initial = initialValue + + val queryConditions = setup.queryConditions + val transactions = Filter.queryOn(realm, queryConditions) + report.addDatedItems(transactions) transactions.forEach { report.addTransaction(it) } + val sessions = Filter.queryOn(realm, queryConditions) + report.addDatedItems(sessions) + + if (setup.virtualBankroll) { + + val options = Calculator.Options(stats = listOf(Stat.NET_RESULT, Stat.HOURLY_RATE, Stat.STANDARD_DEVIATION_HOURLY)) + val group = ComputableGroup(conditions = queryConditions) + val result = Calculator.compute(realm, group, options) + result.computedStat(Stat.NET_RESULT)?.let { + report.netResult = it.value + } + this.computeRiskOfRuin(report, result) + + } else { + + val results = Filter.queryOn(realm, queryConditions) + report.netResult = results.sum("net").toDouble() + + } + + val depositType = TransactionType.getByValue(TransactionType.Value.DEPOSIT, realm) + report.transactionBuckets[depositType.id]?.let { bucket -> + report.depositTotal = bucket.transactions.sumByDouble { it.amount } + } + + val withdrawalType = TransactionType.getByValue(TransactionType.Value.WITHDRAWAL, realm) + report.transactionBuckets[withdrawalType.id]?.let { bucket -> + report.withdrawalTotal = bucket.transactions.sumByDouble { it.amount } + } + + report.generateGraphPointsIfNecessary() + realm.close() return report } + private fun computeRiskOfRuin(report: BankrollReport, results: ComputedResults) { + + val hourlyRate = results.computedStat(Stat.HOURLY_RATE)?.value + val hourlyStandardDeviation = results.computedStat(Stat.STANDARD_DEVIATION_HOURLY)?.value + + if (hourlyRate != null && hourlyStandardDeviation != null) { + + report.riskOfRuin = Stat.riskOfRuin(hourlyRate, hourlyStandardDeviation, report.total) + + } + + } + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt b/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt index 0592c1f8..be3a4b47 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt @@ -1,71 +1,16 @@ package net.pokeranalytics.android.calculus.bankroll -import net.pokeranalytics.android.calculus.interfaces.DatableValue +import android.content.Context +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineDataSet import net.pokeranalytics.android.model.filter.QueryCondition +import net.pokeranalytics.android.model.interfaces.DatedValue import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Transaction +import net.pokeranalytics.android.ui.graph.PALineDataSet import java.util.* import kotlin.collections.HashMap -/** - * A class describing the parameters required to launch a bankroll report - * - */ -class BankrollReportSetup(val bankroll: Bankroll?, val from: Date? = null, val to: Date? = null) { - - val queryConditions: List - get() { - val conditions = mutableListOf() - this.bankroll?.let { - val bankrollCondition = QueryCondition.AnyBankroll(bankroll) - conditions.add(bankrollCondition) - } - this.from?.let { - val fromCondition = QueryCondition.StartedFromDate() - fromCondition.singleValue = it - conditions.add(fromCondition) - } - this.to?.let { - val toCondition = QueryCondition.StartedToDate() - toCondition.singleValue = it - conditions.add(toCondition) - } - - return conditions - } - -} - -class TransactionBucket(useRate: Boolean = false) { - var transactions: MutableList = mutableListOf() - private set - var total: Double = 0.0 - private set - var useRate: Boolean = useRate - private set - - fun addTransaction(transaction: Transaction) { - - this.transactions.add(transaction) - var rate = 1.0 - if (this.useRate) { - rate = transaction.bankroll?.currency?.rate ?: 1.0 - } - - val ratedAmount = rate * transaction.amount - this.total += ratedAmount - - } - -} - -class BRGraphPoint { - - var value: Double = 0.0 - var variation: Double = 0.0 - var date: Date? = null - -} class BankrollReport(setup: BankrollReportSetup) { @@ -80,11 +25,50 @@ class BankrollReport(setup: BankrollReportSetup) { var total: Double = 0.0 private set + /** + * The initial value of the bankroll, or of all bankrolls if virtual is computed + */ + var initial: Double = 0.0 + /** * The net result from poker computables */ var netResult: Double = 0.0 - private set + set(value) { + field = value + this.computeBankrollTotal() + } + + /** + * The net result from transactions + */ + var transactionsNet: Double = 0.0 + set(value) { + field = value + this.computeBankrollTotal() + } + + fun computeBankrollTotal() { + this.total = this.initial + this.netResult + this.transactionsNet + } + + /** + * The sum of all deposits + */ + var depositTotal: Double = 0.0 + set(value) { + field = value + this.netBanked = this.depositTotal + this.withdrawalTotal + } + + /** + * The sum of all withdrawals + */ + var withdrawalTotal: Double = 0.0 + set(value) { + field = value + this.netBanked = this.depositTotal + this.withdrawalTotal + } /** * The difference between withdrawals and deposits @@ -95,16 +79,21 @@ class BankrollReport(setup: BankrollReportSetup) { /** * The risk of ruin */ - var riskOfRuin: Double = 0.0 - private set + var riskOfRuin: Double? = null var transactions: List = mutableListOf() private set var transactionBuckets: HashMap = HashMap() + private set - var evolutionPoints: Array = arrayOf() - var evolutionItems: Array = arrayOf() + var evolutionPoints: MutableList = mutableListOf() + var evolutionItems: MutableList = mutableListOf() + private set + + fun addDatedItems(items: Collection) { + this.evolutionItems.addAll(items) + } fun addTransaction(transaction: Transaction) { @@ -113,7 +102,7 @@ class BankrollReport(setup: BankrollReportSetup) { var bucket = this.transactionBuckets[type.id] if (bucket == null) { - val b = TransactionBucket(this.setup.bankroll == null) + val b = TransactionBucket(this.setup.virtualBankroll) this.transactionBuckets[type.id] = b bucket = b } @@ -126,5 +115,93 @@ class BankrollReport(setup: BankrollReportSetup) { } + fun generateGraphPointsIfNecessary() { + + if (!this.setup.virtualBankroll) { + return + } + + this.evolutionItems.sortBy { it.date } + + this.evolutionItems.forEach { + val point = BRGraphPoint(it.amount, it.date, it) + this.evolutionPoints.add(point) + } + + } + + fun lineDataSet(context: Context): LineDataSet { + + val entries = mutableListOf() + this.evolutionPoints.forEach { + val entry = Entry(it.date.time.toFloat(), it.value.toFloat(), it.data) + entries.add(entry) + } + return PALineDataSet(entries, "", context) + } + +} + +/** + * A class describing the parameters required to launch a bankroll report + * + */ +class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null, val to: Date? = null) { + + val virtualBankroll: Boolean + get() { + return this.bankroll == null + } -} \ No newline at end of file + val queryConditions: List + get() { + val conditions = mutableListOf() + this.bankroll?.let { + val bankrollCondition = QueryCondition.AnyBankroll(bankroll) + conditions.add(bankrollCondition) + } + this.from?.let { + val fromCondition = QueryCondition.StartedFromDate() + fromCondition.singleValue = it + conditions.add(fromCondition) + } + this.to?.let { + val toCondition = QueryCondition.StartedToDate() + toCondition.singleValue = it + conditions.add(toCondition) + } + + return conditions + } + +} + +class TransactionBucket(useRate: Boolean = false) { + + var transactions: MutableList = mutableListOf() + private set + var total: Double = 0.0 + private set + var useRate: Boolean = useRate + private set + + fun addTransaction(transaction: Transaction) { + + this.transactions.add(transaction) + var rate = 1.0 + if (this.useRate) { + rate = transaction.bankroll?.currency?.rate ?: 1.0 + } + + val ratedAmount = rate * transaction.amount + this.total += ratedAmount + + } + +} + +data class BRGraphPoint(var value: Double, var date: Date, var data: Any? = null) { + + var variation: Double = 0.0 + +} diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/interfaces/Datable.kt b/app/src/main/java/net/pokeranalytics/android/calculus/interfaces/Datable.kt deleted file mode 100644 index 7e8855f2..00000000 --- a/app/src/main/java/net/pokeranalytics/android/calculus/interfaces/Datable.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.pokeranalytics.android.calculus.interfaces - -import java.util.* - -interface Datable { - var date: Date -} - -interface DatableValue : Datable { - var value: Double -} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt index 8811f0a4..7ace8c8d 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt @@ -7,6 +7,7 @@ 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.model.realm.Result /** * We want to be able to store filters in the database: @@ -65,6 +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) else -> { throw UnmanagedFilterField("Filterable type fields are not defined for class ${T::class}") } diff --git a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Dated.kt b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Dated.kt new file mode 100644 index 00000000..86cea43b --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Dated.kt @@ -0,0 +1,15 @@ +package net.pokeranalytics.android.model.interfaces + +import java.util.* + +interface Dated { + + var date: Date + +} + +interface DatedValue : Dated { + + var amount: Double + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index 3ea96ad8..c3905e00 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -64,6 +64,10 @@ class PokerAnalyticsMigration : RealmMigration { it.removeField("sessionSet") } + schema.get("Bankroll")?.let { + it.addField("initialValue", Double::class.java).setRequired("initialValue", true) + } + currentVersion++ } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt index 264400fd..cd577725 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt @@ -8,29 +8,12 @@ import io.realm.kotlin.where import net.pokeranalytics.android.R import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.interfaces.SaveValidityStatus -import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.view.RowRepresentable -import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor -import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow -import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow -import net.pokeranalytics.android.util.NULL_TEXT import java.util.* -import kotlin.collections.ArrayList -open class Bankroll() : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { - - companion object { - val rowRepresentation : List by lazy { - val rows = ArrayList() - rows.add(SimpleRow.NAME) - rows.add(BankrollRow.LIVE) - rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.currency)) - rows.add(BankrollRow.CURRENCY) - rows - } - } +open class Bankroll() : RealmObject(), NameManageable, RowRepresentable { @PrimaryKey override var id = UUID.randomUUID().toString() @@ -46,37 +29,16 @@ open class Bankroll() : RealmObject(), NameManageable, StaticRowRepresentableDat // The currency of the bankroll var currency: Currency? = null - override fun getDisplayName(): String { - return this.name - } - - // Row Representable Datasource - override fun adapterRows(): List? { - return Bankroll.rowRepresentation - } + // The initial value of the bankroll + var initialValue: Double = 0.0 - override fun stringForRow(row: RowRepresentable): String { - return when (row) { - SimpleRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT - else -> return super.stringForRow(row) + val rate: Double + get() { + return this.currency?.rate ?: 1.0 } - } - override fun boolForRow(row: RowRepresentable): Boolean { - return when (row) { - BankrollRow.LIVE -> !this.live - else -> super.boolForRow(row) - } - } - - override fun editDescriptors(row: RowRepresentable): ArrayList? { - return when (row) { - SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.name)) - BankrollRow.RATE -> row.editingDescriptors(mapOf()) - else -> { - row.editingDescriptors(mapOf()) - } - } + override fun getDisplayName(): String { + return this.name } override fun updateValue(value: Any?, row: RowRepresentable) { @@ -84,6 +46,9 @@ open class Bankroll() : RealmObject(), NameManageable, StaticRowRepresentableDat SimpleRow.NAME -> this.name = value as String? ?: "" BankrollRow.LIVE -> { this.live = if (value is Boolean) !value else false + } + BankrollRow.INITIAL_VALUE -> { + this.initialValue = value as Double? ?: 0.0 } BankrollRow.CURRENCY -> { //TODO handle a use default currency option diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt index 551cb9eb..7b688316 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt @@ -6,10 +6,22 @@ import io.realm.RealmResults import io.realm.annotations.Ignore import io.realm.annotations.LinkingObjects import io.realm.annotations.RealmClass +import net.pokeranalytics.android.model.filter.Filterable +import net.pokeranalytics.android.model.filter.QueryCondition @RealmClass -open class Result : RealmObject() { +open class Result : RealmObject(), Filterable { + companion object { + + fun fieldNameForQueryType(queryCondition: Class < out QueryCondition>): String? { + Session.fieldNameForQueryType(queryCondition)?.let { + return "sessions.$it" + } + return null + } + + } /** * The buyin amount */ diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index 43037742..835e545e 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -45,7 +45,7 @@ import kotlin.collections.ArrayList typealias BB = Double open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDataSource, RowRepresentable, Timed, - TimeFilterable, Filterable { + TimeFilterable, Filterable, DatedValue { enum class Type { CASH_GAME, @@ -308,10 +308,10 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat get() { val bb = this.cgBigBlind val result = this.result - if (bb != null && result != null) { - return result.net / bb + return if (bb != null && result != null) { + result.net / bb } else { - return 0.0 + 0.0 } } @@ -326,6 +326,21 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat return noh * hd } + + // DatedValue + + @Ignore + override var date: Date = Date() + get() { + return this.startDate ?: this.creationDate + } + + @Ignore + override var amount: Double = 0.0 + get() { + return this.computableResult?.ratedNet ?: 0.0 + } + /** * Pre-compute various stats */ @@ -973,10 +988,10 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } } - return LegendView.Values(this.entryTitle, left, right) + LegendView.Values(this.entryTitle, left, right) } else -> { - return super.legendValues(stat, entry) + super.legendValues(stat, entry) } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt index d7291dc7..481a9444 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt @@ -7,6 +7,7 @@ import io.realm.annotations.PrimaryKey import net.pokeranalytics.android.R import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.QueryCondition +import net.pokeranalytics.android.model.interfaces.DatedValue import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.interfaces.SaveValidityStatus import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource @@ -17,7 +18,7 @@ import java.util.* import kotlin.collections.ArrayList -open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, Filterable { +open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, Filterable, DatedValue { companion object { val rowRepresentation: List by lazy { @@ -42,10 +43,10 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo var bankroll: Bankroll? = null // The amount of the transaction - var amount: Double = 0.0 + override var amount: Double = 0.0 // The date of the transaction - var date: Date = Date() + override var date: Date = Date() // The type of the transaction var type: TransactionType? = null diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt index eba342a5..0a55ef46 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt @@ -14,65 +14,77 @@ import kotlin.collections.ArrayList open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { - companion object { - val rowRepresentation : List by lazy { - val rows = ArrayList() - rows.add(SimpleRow.NAME) - rows.addAll(TransactionTypeRow.values()) - rows - } - } - - @PrimaryKey - override var id = UUID.randomUUID().toString() - - // The name of the transaction type - override var name: String = "" - - // Whether or not the amount is added, or subtracted to the bankroll total - var additive: Boolean = false - - // Whether or not the type can be deleted by the user - var lock: Boolean = false - - // The predefined kind, if necessary, like: Withdrawal, deposit, or tips - var kind: Int? = null - - override fun getDisplayName(): String { - return this.name - } - - override fun adapterRows(): List? { - return TransactionType.rowRepresentation - } - - override fun stringForRow(row: RowRepresentable): String { - return when (row) { - SimpleRow.NAME -> this.name - else -> return super.stringForRow(row) - } - } - - override fun editDescriptors(row: RowRepresentable): ArrayList? { - return row.editingDescriptors(mapOf("defaultValue" to this.name)) - } - - override fun updateValue(value: Any?, row: RowRepresentable) { - when (row) { - SimpleRow.NAME -> this.name = value as String? ?: "" - } - } - - override fun isValidForDelete(realm: Realm): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getFailedDeleteMessage(): Int { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } -} -enum class TransactionKind { - WITHDRAWAL, - DEPOSIT + enum class Value(val additive: Boolean) { + WITHDRAWAL(false), + DEPOSIT(true), + BONUS(true) + } + + companion object { + val rowRepresentation: List by lazy { + val rows = ArrayList() + rows.add(SimpleRow.NAME) + rows.addAll(TransactionTypeRow.values()) + rows + } + + fun getByValue(value: Value, realm: Realm): TransactionType { + val type = realm.where(TransactionType::class.java).equalTo("kind", value.ordinal).findFirst() + type?.let { + return it + } + throw IllegalStateException("Transaction type ${value.name} should exist in database!") + } + + } + + @PrimaryKey + override var id = UUID.randomUUID().toString() + + // The name of the transaction type + override var name: String = "" + + // Whether or not the amount is added, or subtracted to the bankroll total + var additive: Boolean = false + + // Whether or not the type can be deleted by the user + var lock: Boolean = false + + // The predefined kind, if necessary, like: Withdrawal, deposit, or tips + var kind: Int? = null + + override fun getDisplayName(): String { + return this.name + } + + override fun adapterRows(): List? { + return rowRepresentation + } + + override fun stringForRow(row: RowRepresentable): String { + return when (row) { + SimpleRow.NAME -> this.name + else -> return super.stringForRow(row) + } + } + + override fun editDescriptors(row: RowRepresentable): ArrayList? { + return row.editingDescriptors(mapOf("defaultValue" to this.name)) + } + + override fun updateValue(value: Any?, row: RowRepresentable) { + when (row) { + SimpleRow.NAME -> this.name = value as String? ?: "" + } + } + + override fun isValidForDelete(realm: Realm): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getFailedDeleteMessage(): Int { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } } + diff --git a/app/src/main/java/net/pokeranalytics/android/model/utils/Seed.kt b/app/src/main/java/net/pokeranalytics/android/model/utils/Seed.kt index 2bc66fc3..a0d2d371 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/utils/Seed.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/utils/Seed.kt @@ -3,12 +3,10 @@ package net.pokeranalytics.android.model.utils import android.content.Context import io.realm.Realm import io.realm.kotlin.where -import net.pokeranalytics.android.model.realm.Bankroll +import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.Currency -import net.pokeranalytics.android.model.realm.TournamentFeature -import java.util.* -import net.pokeranalytics.android.model.realm.Game import net.pokeranalytics.android.util.UserDefaults +import java.util.* class Seed(var context:Context) : Realm.Transaction { @@ -17,6 +15,7 @@ class Seed(var context:Context) : Realm.Transaction { this.createDefaultGames(realm) this.createDefaultTournamentFeatures(realm) this.createDefaultCurrencyAndBankroll(realm) + this.createDefaultTransactionTypes(realm) } private fun createDefaultTournamentFeatures(realm: Realm) { @@ -55,4 +54,15 @@ class Seed(var context:Context) : Realm.Transaction { realm.insertOrUpdate(game) } } + + private fun createDefaultTransactionTypes(realm: Realm) { + TransactionType.Value.values().forEachIndexed { index, value -> + val type = TransactionType() + type.additive = value.additive + type.kind = index + type.lock = true + realm.insertOrUpdate(type) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDataFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDataFragment.kt index 8079f8e9..7f15c747 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDataFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDataFragment.kt @@ -18,8 +18,8 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.util.NULL_TEXT -import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.UserDefaults +import net.pokeranalytics.android.util.extensions.toCurrency import net.pokeranalytics.android.util.extensions.toRate import retrofit2.Call import retrofit2.Response @@ -95,6 +95,9 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS NULL_TEXT } } + BankrollRow.INITIAL_VALUE -> { + this.bankroll.initialValue.toCurrency() + } BankrollRow.RATE -> { val rate = this.bankroll.currency?.rate ?: 1.0 rate.toRate() @@ -114,6 +117,9 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS override fun editDescriptors(row: RowRepresentable): ArrayList? { return when (row) { SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.bankroll.name)) + BankrollRow.INITIAL_VALUE -> { + row.editingDescriptors(mapOf("defaultValue" to this.bankroll.initialValue)) + } BankrollRow.RATE -> { val rate = this.bankroll.currency?.rate row.editingDescriptors(mapOf("defaultValue" to rate)) @@ -163,6 +169,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS rows.clear() rows.add(SimpleRow.NAME) rows.add(BankrollRow.LIVE) + rows.add(BankrollRow.INITIAL_VALUE) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.currency)) rows.add(BankrollRow.CURRENCY) if (this.shouldShowCurrencyRate) { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt index f5d78f5a..ba10376a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt @@ -159,12 +159,6 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele finishActivityWithResult(uniqueIdentifier) } -// if (managedItem is Bankroll) { -// managedItem.currency?.refreshRelatedRatedValues(it) -// } -// -// val uniqueIdentifier = (managedItem as Savable).id -// finishActivityWithResult(uniqueIdentifier) } } else -> { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt index f5cfdc6a..1e3b3152 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/BankrollRow.kt @@ -11,6 +11,7 @@ import net.pokeranalytics.android.ui.view.RowViewType enum class BankrollRow : RowRepresentable, DefaultEditDataSource { LIVE, + INITIAL_VALUE, CURRENCY, RATE, REFRESH_RATE; @@ -19,6 +20,7 @@ enum class BankrollRow : RowRepresentable, DefaultEditDataSource { get() { return when (this) { LIVE -> R.string.online + INITIAL_VALUE -> R.string.initial_value CURRENCY -> R.string.currency RATE -> R.string.rate REFRESH_RATE -> R.string.refresh_rate @@ -29,6 +31,7 @@ enum class BankrollRow : RowRepresentable, DefaultEditDataSource { get() { return when (this) { LIVE -> RowViewType.TITLE_SWITCH.ordinal + INITIAL_VALUE -> RowViewType.TITLE_VALUE.ordinal CURRENCY -> RowViewType.TITLE_VALUE_ARROW.ordinal RATE -> RowViewType.TITLE_VALUE.ordinal REFRESH_RATE -> RowViewType.ROW_BUTTON.ordinal @@ -39,6 +42,7 @@ enum class BankrollRow : RowRepresentable, DefaultEditDataSource { get() { return when (this) { LIVE -> BottomSheetType.NONE + INITIAL_VALUE -> BottomSheetType.NUMERIC_TEXT CURRENCY -> BottomSheetType.NONE RATE -> BottomSheetType.NUMERIC_TEXT REFRESH_RATE -> BottomSheetType.NONE @@ -47,6 +51,13 @@ enum class BankrollRow : RowRepresentable, DefaultEditDataSource { override fun editingDescriptors(map: Map): ArrayList? { return when (this) { + INITIAL_VALUE -> { + val defaultValue : Any? by map + arrayListOf( + RowRepresentableEditDescriptor(defaultValue, R.string.initial_value, InputType.TYPE_CLASS_NUMBER + or InputType.TYPE_NUMBER_FLAG_DECIMAL) + ) + } RATE -> { val defaultValue : Any? by map arrayListOf( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a6a2e029..1a1952fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,6 +8,7 @@ More Variant Line + Initial Value