From c78ede97fad1edd0c73bbbd85a125c5947d03890 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 15 May 2019 15:13:53 +0200 Subject: [PATCH] Add custom report launching + Savable enums + Tests --- .../android/calculus/Calculator.kt | 18 ++- .../pokeranalytics/android/calculus/Report.kt | 16 +-- .../pokeranalytics/android/calculus/Stat.kt | 104 ++++++++++-------- .../android/exceptions/Exceptions.kt | 5 +- .../pokeranalytics/android/model/Criteria.kt | 61 +++++----- .../android/model/interfaces/Manageable.kt | 4 +- .../android/model/interfaces/Timed.kt | 2 +- .../migrations/PokerAnalyticsMigration.kt | 8 ++ .../android/model/realm/ReportSetup.kt | 56 ++++++++-- .../android/model/realm/Session.kt | 5 +- .../android/model/realm/SessionSet.kt | 9 +- .../android/ui/activity/HomeActivity.kt | 2 +- .../ui/adapter/RowRepresentableDataSource.kt | 2 +- .../android/ui/fragment/GraphFragment.kt | 6 +- .../ui/fragment/ReportCreationFragment.kt | 8 +- .../android/ui/fragment/ReportsFragment.kt | 77 ++++++++----- .../ui/fragment/report/TableReportFragment.kt | 2 +- .../android/ui/graph/GraphUnderlyingEntry.kt | 5 + .../android/ui/view/LegendView.kt | 4 +- .../android/ui/view/RowRepresentable.kt | 2 +- .../android/ui/view/RowViewType.kt | 2 +- .../util/enumerations/SavableEnumeration.kt | 23 ++++ app/src/main/res/menu/navigation_home.xml | 2 +- .../pokeranalytics/android/SavableEnumTest.kt | 21 ++++ 24 files changed, 297 insertions(+), 147 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/util/enumerations/SavableEnumeration.kt create mode 100644 app/src/test/java/net/pokeranalytics/android/SavableEnumTest.kt 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 1f6612cf..3633315c 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt @@ -20,7 +20,7 @@ import kotlin.math.max import kotlin.math.min /** - * The class performing stats computation + * The class performing statIds computation */ class Calculator { @@ -64,7 +64,7 @@ class Calculator { } /** - * The way the stats are going to be displayed + * The way the statIds are going to be displayed */ enum class Display : RowRepresentable { TABLE, @@ -139,7 +139,13 @@ class Calculator { val rs = ReportSetup() rs.name = name rs.display = this.display.ordinal - + this.stats.forEach { + rs.statIds.add(it.uniqueIdentifier) + } + this.criterias.forEach { + rs.criteriaIds.add(it.uniqueIdentifier) + } + rs.filter = this._filter return rs } @@ -207,7 +213,7 @@ class Calculator { } /** - * Computes all stats for list of Session sessionGroup + * Computes all statIds for list of Session sessionGroup */ fun computeGroups(realm: Realm, groups: List, options: Options = Options()): Report { @@ -218,7 +224,7 @@ class Calculator { // Clean existing computables / sessionSets if group is reused group.cleanup() - // Computes actual sessionGroup stats + // Computes actual sessionGroup statIds val results: ComputedResults = this.compute(realm, group, options) // Computes the compared sessionGroup if existing @@ -246,7 +252,7 @@ class Calculator { } /** - * Computes stats for a SessionSet + * Computes statIds for a SessionSet */ fun compute(realm: Realm, computableGroup: ComputableGroup, options: Options = Options()): ComputedResults { 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 fe2258cf..0307f39a 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Report.kt @@ -97,9 +97,9 @@ class Report(var options: Calculator.Options) { */ class ComputableGroup(query: Query, stats: List? = null) { -// constructor(query: Query, stats: List? = null) : this(query.name, query.conditions) +// constructor(query: Query, statIds: List? = null) : this(query.name, query.conditions) // -// private constructor(name: String = "", conditions: List = listOf(), stats: List? = null) +// private constructor(name: String = "", conditions: List = listOf(), statIds: List? = null) var query: Query = query @@ -165,7 +165,7 @@ class ComputableGroup(query: Query, stats: List? = null) { } /** - * The list of stats to display + * The list of statIds to display */ var stats: List? = stats @@ -175,7 +175,7 @@ class ComputableGroup(query: Query, stats: List? = null) { var comparedGroup: ComputableGroup? = null /** - * The computed stats of the comparable sessionGroup + * The computed statIds of the comparable sessionGroup */ var comparedComputedResults: ComputedResults? = null @@ -194,14 +194,14 @@ class ComputableGroup(query: Query, stats: List? = null) { class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValues: Boolean = false) : GraphUnderlyingEntry { /** - * The session group used to computed the stats + * The session group used to computed the statIds */ var group: ComputableGroup = group - // The computed stats of the sessionGroup + // The computed statIds of the sessionGroup private var _computedStats: MutableMap = mutableMapOf() - // The map containing all evolution numericValues for all stats + // The map containing all evolution numericValues for all statIds private var _evolutionValues: MutableMap> = mutableMapOf() private var shouldManageMultiGroupProgressValues = shouldManageMultiGroupProgressValues @@ -245,7 +245,7 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu } /** - * Adds a [computedStat] to the list of stats + * Adds a [computedStat] to the list of statIds * Also computes evolution values using the previously computed values */ private fun addComputedStat(computedStat: ComputedStat) { 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 431db5cf..7179990b 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt @@ -3,10 +3,11 @@ package net.pokeranalytics.android.calculus import android.content.Context import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.FormattingException -import net.pokeranalytics.android.model.interfaces.Timed import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.util.NULL_TEXT +import net.pokeranalytics.android.util.enumerations.IntIdentifiable +import net.pokeranalytics.android.util.enumerations.IntSearchable import net.pokeranalytics.android.util.extensions.formatted import net.pokeranalytics.android.util.extensions.formattedHourlyDuration import net.pokeranalytics.android.util.extensions.toCurrency @@ -17,46 +18,46 @@ class StatFormattingException(message: String) : Exception(message) { } -class ObjectIdentifier(var id: String, var clazz: Class) { - -} - /** * An enum representing all the types of Session statistics */ -enum class Stat : RowRepresentable { - - NET_RESULT, - BB_NET_RESULT, - HOURLY_RATE, - AVERAGE, - NUMBER_OF_SETS, - NUMBER_OF_GAMES, - HOURLY_DURATION, - AVERAGE_HOURLY_DURATION, - NET_BB_PER_100_HANDS, - HOURLY_RATE_BB, - AVERAGE_NET_BB, - WIN_RATIO, - AVERAGE_BUYIN, - ROI, - STANDARD_DEVIATION, - STANDARD_DEVIATION_HOURLY, - STANDARD_DEVIATION_BB_PER_100_HANDS, - HANDS_PLAYED, - LOCATIONS_PLAYED, - LONGEST_STREAKS, - MAXIMUM_NETRESULT, - MINIMUM_NETRESULT, - MAXIMUM_DURATION, - DAYS_PLAYED, - WINNING_SESSION_COUNT, - BB_SESSION_COUNT, - TOTAL_BUYIN, - RISK_OF_RUIN, +enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepresentable { + + NET_RESULT(1), + BB_NET_RESULT(2), + HOURLY_RATE(3), + AVERAGE(4), + NUMBER_OF_SETS(5), + NUMBER_OF_GAMES(6), + HOURLY_DURATION(7), + AVERAGE_HOURLY_DURATION(8), + NET_BB_PER_100_HANDS(9), + HOURLY_RATE_BB(10), + AVERAGE_NET_BB(11), + WIN_RATIO(12), + AVERAGE_BUYIN(13), + ROI(14), + STANDARD_DEVIATION(15), + STANDARD_DEVIATION_HOURLY(16), + STANDARD_DEVIATION_BB_PER_100_HANDS(17), + HANDS_PLAYED(18), + LOCATIONS_PLAYED(19), + LONGEST_STREAKS(20), + MAXIMUM_NETRESULT(21), + MINIMUM_NETRESULT(22), + MAXIMUM_DURATION(23), + DAYS_PLAYED(24), + WINNING_SESSION_COUNT(25), + BB_SESSION_COUNT(26), + TOTAL_BUYIN(27), + RISK_OF_RUIN(28), ; - companion object { + companion object : IntSearchable { + + override fun valuesInternal(): Array { + return values() + } val userSelectableList: List get() { @@ -65,7 +66,7 @@ enum class Stat : RowRepresentable { val evolutionValuesList: List get() { - return values().filter { it.hasEvolutionValues } + return values().filter { it.hasProgressValues } } fun returnOnInvestment(netResult: Double, buyin: Double): Double? { @@ -172,7 +173,7 @@ enum class Stat : RowRepresentable { } } - val threshold: Double + private val threshold: Double get() { return when (this) { WIN_RATIO -> 50.0 @@ -181,6 +182,9 @@ enum class Stat : RowRepresentable { } + /** + * Returns a label used to display the legend right value, typically a total or an average + */ fun cumulativeLabelResId(context: Context): String { val resId = when (this) { AVERAGE, AVERAGE_HOURLY_DURATION, NET_BB_PER_100_HANDS, @@ -201,6 +205,9 @@ enum class Stat : RowRepresentable { } } + /** + * Returns the different available aggregation type for each statistic + */ val aggregationTypes: List get() { return when (this) { @@ -215,7 +222,10 @@ enum class Stat : RowRepresentable { } } - val hasEvolutionGraph: Boolean + /** + * Returns if the stat has an evolution graph + */ + val hasProgressGraph: Boolean get() { return when (this) { HOURLY_DURATION, AVERAGE_HOURLY_DURATION, @@ -224,7 +234,10 @@ enum class Stat : RowRepresentable { } } - val significantIndividualValue: Boolean + /** + * Returns if the stat has a significant value to display in a progress graph + */ + val graphSignificantIndividualValue: Boolean get() { return when (this) { WIN_RATIO, NUMBER_OF_SETS, NUMBER_OF_GAMES, STANDARD_DEVIATION, HOURLY_DURATION -> false @@ -232,7 +245,10 @@ enum class Stat : RowRepresentable { } } - val shouldShowNumberOfSessions: Boolean + /** + * Returns if the stat graph should show the number of sessions + */ + val graphShouldShowNumberOfSessions: Boolean get() { return when (this) { NUMBER_OF_GAMES, NUMBER_OF_SETS -> false @@ -240,7 +256,7 @@ enum class Stat : RowRepresentable { } } - val showXAxisZero: Boolean + val graphShowsXAxisZero: Boolean get() { return when (this) { HOURLY_DURATION -> true @@ -248,7 +264,7 @@ enum class Stat : RowRepresentable { } } - val showYAxisZero: Boolean + val graphShowsYAxisZero: Boolean get() { return when (this) { HOURLY_DURATION -> true @@ -264,7 +280,7 @@ enum class Stat : RowRepresentable { } } - private val hasEvolutionValues: Boolean + private val hasProgressValues: Boolean get() { return when (this) { NET_RESULT, NET_BB_PER_100_HANDS, HOURLY_RATE_BB, diff --git a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt index 1eabf395..7716e745 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -8,6 +8,9 @@ class RowRepresentableEditDescriptorException(message: String) : Exception(messa class ConfigurationException(message: String) : Exception(message) +class EnumIdentifierNotFoundException(message: String) : Exception(message) +class MisconfiguredSavableEnumException(message: String) : Exception(message) + sealed class PokerAnalyticsException(message: String) : Exception(message) { object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryCondition") object FilterElementUnknownSectionName: PokerAnalyticsException(message = "No filterElement section name was found to identify the queryCondition") @@ -21,4 +24,4 @@ sealed class PokerAnalyticsException(message: String) : Exception(message) { data class QueryValueMapMissingKeys(val missingKeys: List) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys") data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no queryWith type for $filterElementRow") data class MissingFieldNameForQueryCondition(val name: String) : PokerAnalyticsException(message = "Missing fieldname for QueryCondition ${name}") -} \ No newline at end of file +} diff --git a/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt b/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt index 0c36d072..c87a0898 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/Criteria.kt @@ -21,6 +21,8 @@ import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.util.enumerations.IntIdentifiable +import net.pokeranalytics.android.util.enumerations.IntSearchable fun List.combined(): List { val comparatorList = ArrayList>() @@ -54,21 +56,21 @@ fun getCombinations(queries: List>): List { return combinations } -sealed class Criteria : RowRepresentable { +sealed class Criteria(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepresentable { - abstract class RealmCriteria : Criteria() { + abstract class RealmCriteria(uniqueIdentifier: Int) : Criteria(uniqueIdentifier) { inline fun comparison(): List { return compare, T>() } } - abstract class SimpleCriteria(private val conditions: List) : Criteria() { + abstract class SimpleCriteria(private val conditions: List, uniqueIdentifier: Int) : Criteria(uniqueIdentifier) { fun comparison(): List { return conditions.map { Query(it) } } } - abstract class ListCriteria : Criteria() { + abstract class ListCriteria(uniqueIdentifier: Int) : Criteria(uniqueIdentifier) { inline fun , reified S : Comparable> comparison(): List { QueryCondition.distinct()?.let { val values = it.mapNotNull { session -> @@ -98,32 +100,32 @@ sealed class Criteria : RowRepresentable { } - object Bankrolls : RealmCriteria() - object Games : RealmCriteria() - object TournamentNames : RealmCriteria() - object Locations : RealmCriteria() - object TournamentFeatures : RealmCriteria() - object TransactionTypes : RealmCriteria() - object Limits : ListCriteria() - object TableSizes : ListCriteria() - object TournamentTypes : ListCriteria() + object Bankrolls : RealmCriteria(1) + object Games : RealmCriteria(2) + object TournamentNames : RealmCriteria(3) + object Locations : RealmCriteria(4) + object TournamentFeatures : RealmCriteria(5) + object TransactionTypes : RealmCriteria(6) + object Limits : ListCriteria(7) + object TableSizes : ListCriteria(8) + object TournamentTypes : ListCriteria(9) object MonthsOfYear : SimpleCriteria(List(12) { index -> QueryCondition.AnyMonthOfYear().apply { listOfValues = arrayListOf(index) } - }) + }, 10) object DaysOfWeek : SimpleCriteria(List(7) { index -> QueryCondition.AnyDayOfWeek().apply { listOfValues = arrayListOf(index + 1) } - }) - - object SessionTypes : SimpleCriteria(listOf(QueryCondition.IsCash, QueryCondition.IsTournament)) - object BankrollTypes : SimpleCriteria(listOf(QueryCondition.IsLive, QueryCondition.IsOnline)) - object DayPeriods : SimpleCriteria(listOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd)) - object Years : ListCriteria() - object AllMonthsUpToNow : ListCriteria() - object Blinds : ListCriteria() - object TournamentFees : ListCriteria() - object Cash : SimpleCriteria(listOf(QueryCondition.IsCash)) - object Tournament : SimpleCriteria(listOf(QueryCondition.IsTournament)) + }, 11) + + object SessionTypes : SimpleCriteria(listOf(QueryCondition.IsCash, QueryCondition.IsTournament), 12) + object BankrollTypes : SimpleCriteria(listOf(QueryCondition.IsLive, QueryCondition.IsOnline), 13) + object DayPeriods : SimpleCriteria(listOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd), 14) + object Years : ListCriteria(15) + object AllMonthsUpToNow : ListCriteria(16) + object Blinds : ListCriteria(17) + object TournamentFees : ListCriteria(18) + object Cash : SimpleCriteria(listOf(QueryCondition.IsCash), 19) + object Tournament : SimpleCriteria(listOf(QueryCondition.IsTournament), 20) val queries: List get() { @@ -225,7 +227,8 @@ sealed class Criteria : RowRepresentable { } } - companion object { + companion object : IntSearchable { + inline fun , reified T : NameManageable> compare(): List { val objects = mutableListOf() val realm = Realm.getDefaultInstance() @@ -263,7 +266,11 @@ sealed class Criteria : RowRepresentable { ) } - + // SavableEnum + override fun valuesInternal(): Array { + return all.toTypedArray() + } } + } diff --git a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt index 2620928c..3841e81c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt @@ -46,12 +46,12 @@ interface NameManageable : Manageable { /** - * An interface associate a unique identifier to an object + * An interface associate a unique uniqueIdentifier to an object */ interface Identifiable : RealmModel { /** - * A unique identifier getter + * A unique uniqueIdentifier getter */ var id: String } diff --git a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt index aa2741dd..1123f298 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt @@ -1,7 +1,7 @@ package net.pokeranalytics.android.model.interfaces -import net.pokeranalytics.android.calculus.ObjectIdentifier import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry +import net.pokeranalytics.android.ui.graph.ObjectIdentifier import java.util.* interface Timed : GraphUnderlyingEntry, Identifiable { 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 a9127c93..d10af366 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 @@ -112,6 +112,14 @@ class PokerAnalyticsMigration : RealmMigration { it.addField("duplicateValue", Boolean::class.java) it.addRealmListField("entries", CustomFieldEntry::class.java) } + schema.get("ReportSetup")?.let { + it.addRealmListField("statIds", Int::class.java) + it.addRealmListField("criteriaIds", Int::class.java) + it.removeField("filters") + schema.get("Filter")?.let { filterSchema -> + it.addRealmObjectField("filter", filterSchema) + } + } currentVersion++ } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt index 246adcf1..ccbaca2c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt @@ -2,17 +2,17 @@ package net.pokeranalytics.android.model.realm import io.realm.RealmList import io.realm.RealmObject +import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey +import net.pokeranalytics.android.calculus.Calculator +import net.pokeranalytics.android.calculus.Stat +import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.view.RowViewType import java.util.* -enum class ReportDisplay : RowRepresentable { - FIGURES, - EVO_GRAPH, - COMPARISON_GRAPH -} -open class ReportSetup : RealmObject() { +open class ReportSetup : RealmObject(), RowRepresentable { @PrimaryKey var id = UUID.randomUUID().toString() @@ -21,14 +21,46 @@ open class ReportSetup : RealmObject() { var name: String = "" // The type of display of the report - var display: Int = ReportDisplay.FIGURES.ordinal + var display: Int = Calculator.Options.Display.TABLE.ordinal + + /** + * A list of statIds to compute + * Must contain at least 1 + */ + var statIds: RealmList = RealmList() + + /** + * An optional list of criteriaIds to compare statIds + */ + var criteriaIds: RealmList = RealmList() + + /** + * An optional filter to narrow the results + */ + var filter: Filter? = null - // @todo define the configuration options + // RowRepresentable + override fun getDisplayName(): String { + return this.name + } -// var criteria: List = listOf() -// var stats: List = listOf() + @Ignore + override val viewType: Int = RowViewType.TITLE_ARROW.ordinal - // The filters associated with the report - var filters: RealmList = RealmList() + /** + * Returns the Options based on the ReportSetup parameters + */ + val options: Calculator.Options + get() { + val stats = this.statIds.map { Stat.valueByIdentifier(it) } + val criteria = this.criteriaIds.map { Criteria.valueByIdentifier(it) } + return Calculator.Options( + display = Calculator.Options.Display.values()[this.display], + stats = stats, + criterias = criteria, + filter = this.filter, + userGenerated = true + ) + } } 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 5d5061c1..18c02c44 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 @@ -28,6 +28,7 @@ import net.pokeranalytics.android.model.utils.SessionSetManager import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException import net.pokeranalytics.android.ui.fragment.GraphFragment +import net.pokeranalytics.android.ui.graph.ObjectIdentifier import net.pokeranalytics.android.ui.view.* import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRow @@ -344,7 +345,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } /** - * Pre-compute various stats + * Pre-compute various statIds */ fun computeStats() { @@ -961,7 +962,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat } } ?: run { - throw java.lang.IllegalStateException("Asking for stats on Session without Result") + throw java.lang.IllegalStateException("Asking for statIds on Session without Result") } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt index 52b998c6..fea9c1c8 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt @@ -3,16 +3,15 @@ package net.pokeranalytics.android.model.realm 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 net.pokeranalytics.android.calculus.ObjectIdentifier import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.StatFormattingException import net.pokeranalytics.android.calculus.TextFormat import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.interfaces.Timed +import net.pokeranalytics.android.ui.graph.ObjectIdentifier import net.pokeranalytics.android.util.NULL_TEXT import java.text.DateFormat import java.util.* @@ -74,8 +73,10 @@ open class SessionSet() : RealmObject(), Timed, Filterable { var ratedNet: Double = 0.0 - @Ignore - val hourlyRate: Double = this.ratedNet / this.hourlyDuration + val hourlyRate: Double + get() { + return this.ratedNet / this.hourlyDuration + } var estimatedHands: Double = 0.0 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt index 7de5d8f1..bae6aa87 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt @@ -175,7 +175,7 @@ class HomeActivity : PokerAnalyticsActivity() { homeMenu?.findItem(R.id.queryWith)?.isVisible = false } 1 -> { - toolbar.titleResId = getString(R.string.stats) + toolbar.titleResId = getString(R.string.statIds) homeMenu?.findItem(R.id.queryWith)?.isVisible = false } 2 -> { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt b/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt index b06073a2..f02f7f96 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt @@ -147,7 +147,7 @@ interface DisplayableDataSource { } /** - * Returns an action icon identifier for a specific row + * Returns an action icon uniqueIdentifier for a specific row */ fun actionIconForRow(row: RowRepresentable): Int? { return 0 diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt index b755fba8..31821a0b 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt @@ -13,12 +13,12 @@ import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBub import com.github.mikephil.charting.listener.OnChartValueSelectedListener import kotlinx.android.synthetic.main.fragment_graph.* import net.pokeranalytics.android.R -import net.pokeranalytics.android.calculus.ObjectIdentifier import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.graph.AxisFormatting import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry +import net.pokeranalytics.android.ui.graph.ObjectIdentifier import net.pokeranalytics.android.ui.graph.setStyle import net.pokeranalytics.android.ui.view.LegendView import net.pokeranalytics.android.ui.view.MultiLineLegendView @@ -143,10 +143,10 @@ class GraphFragment : RealmFragment(), OnChartValueSelectedListener { val barChart = BarChart(context) barChart.setOnChartValueSelectedListener(this) - if (stat.showXAxisZero) { + if (stat.graphShowsXAxisZero) { barChart.xAxis.axisMinimum = 0.0f } - if (stat.showYAxisZero) { + if (stat.graphShowsYAxisZero) { barChart.axisLeft.axisMinimum = 0.0f } this.chartView = barChart diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt index 5ddd5587..11cc28d9 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt @@ -54,14 +54,18 @@ class ReportCreationFragment : RealmFragment(), RowRepresentableDataSource, RowR this.assistant.nextStep() if (this.assistant.step == Assistant.Step.FINALIZE) { - // launch report +// getRealm().executeTransaction { +// val rs = this.assistant.options.reportSetup("test") +// it.insert(rs) +// } + + // launch report this.finishActivityWithOptions(this.assistant.options) } else { this.updateUIWithCurrentStep() } - } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt index e9ae0cc6..54b3be17 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt @@ -9,6 +9,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.recyclerview.widget.LinearLayoutManager import io.realm.Realm +import io.realm.RealmResults import kotlinx.android.synthetic.main.fragment_data_list.* import kotlinx.android.synthetic.main.fragment_stats.recyclerView import kotlinx.coroutines.Dispatchers @@ -19,21 +20,27 @@ import net.pokeranalytics.android.calculus.Calculator import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.combined +import net.pokeranalytics.android.model.realm.ReportSetup import net.pokeranalytics.android.ui.activity.ComparisonReportActivity -import net.pokeranalytics.android.ui.activity.ReportCreationActivity import net.pokeranalytics.android.ui.activity.ProgressReportActivity +import net.pokeranalytics.android.ui.activity.ReportCreationActivity import net.pokeranalytics.android.ui.activity.TableReportActivity import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource -import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment +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.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.ReportRow import timber.log.Timber import java.util.* -import kotlin.collections.ArrayList -class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { +class ReportsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { + + private lateinit var reportsAdapter: RowRepresentableAdapter + private lateinit var reportSetups: RealmResults + private var adapterRows = mutableListOf() companion object { @@ -47,19 +54,12 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour return fragment } - val rowRepresentation: List by lazy { - val rows = ArrayList() - rows.addAll(ReportRow.getRows()) - rows - } } - private lateinit var reportsAdapter: RowRepresentableAdapter - - // Life Cycle override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + super.onCreateView(inflater, container, savedInstanceState) return inflater.inflate(R.layout.fragment_reports, container, false) } @@ -67,6 +67,7 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour super.onViewCreated(view, savedInstanceState) initData() initUI() + this.updateRows() } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -80,19 +81,6 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour } } - // Rows - - override fun adapterRows(): List? { - return rowRepresentation - } - - override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { - super.onRowSelected(position, row, fromAction) - if (row is ReportRow) { - val reportName = row.localizedTitle(requireContext()) - launchComputation(row.criteria, reportName) - } - } // Business @@ -100,6 +88,10 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour * Init data */ private fun initData() { + this.reportSetups = getRealm().where(ReportSetup::class.java).findAll().sort("name") + this.reportSetups.addChangeListener { _, _ -> + this.updateRows() + } } /** @@ -123,6 +115,36 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour } + // Rows + + fun updateRows() { + this.adapterRows.clear() + if (this.reportSetups.size > 0) { + adapterRows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.custom)) + adapterRows.addAll(this.reportSetups) + } + adapterRows.addAll(ReportRow.getRows()) + this.reportsAdapter.notifyDataSetChanged() + } + + override fun adapterRows(): List? { + return this.adapterRows + } + + override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { + super.onRowSelected(position, row, fromAction) + + when (row) { + is ReportRow -> { + val reportName = row.localizedTitle(requireContext()) + launchComputation(row.criteria, reportName) + } + is ReportSetup -> { + launchReportWithOptions(row.options, row.name) + } + } + } + /** * Launch computation */ @@ -145,6 +167,9 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour } + /** + * Launch and display a report with some [options] and a [reportName] + */ private fun launchReportWithOptions(options: Calculator.Options, reportName: String) { showLoader() @@ -176,8 +201,6 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour Timber.d("Report type not handled at the moment") } } - - } } realm.close() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt index 69f065a7..8ceea198 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt @@ -157,7 +157,7 @@ open class TableReportFragment : ResultsObserverFragment(), StaticRowRepresentab return } - if (row is StatRow && row.stat.hasEvolutionGraph) { + if (row is StatRow && row.stat.hasProgressGraph) { // queryWith groups val groupResults = this.report?.results?.filter { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphUnderlyingEntry.kt b/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphUnderlyingEntry.kt index addddf61..bc0813f3 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphUnderlyingEntry.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/graph/GraphUnderlyingEntry.kt @@ -4,10 +4,15 @@ import android.content.Context import com.github.mikephil.charting.data.Entry import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.TextFormat +import net.pokeranalytics.android.model.interfaces.Timed import net.pokeranalytics.android.ui.fragment.GraphFragment import net.pokeranalytics.android.ui.view.DefaultLegendValues import net.pokeranalytics.android.ui.view.LegendContent +class ObjectIdentifier(var id: String, var clazz: Class) { + +} + interface GraphUnderlyingEntry { val entryTitle: String diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/LegendView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/LegendView.kt index 89608a15..c04c1aed 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/LegendView.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/LegendView.kt @@ -77,7 +77,7 @@ open class LegendView : FrameLayout { this.counter.isVisible = false } GraphFragment.Style.LINE -> { - if (stat.significantIndividualValue) { + if (stat.graphSignificantIndividualValue) { this.stat1Name.text = stat.localizedTitle(context) this.stat2Name.text = stat.cumulativeLabelResId(context) } else { @@ -88,7 +88,7 @@ open class LegendView : FrameLayout { counter?.let { val counterText = "$it ${context.getString(R.string.sessions)}" this.counter.text = counterText - this.counter.isVisible = stat.shouldShowNumberOfSessions + this.counter.isVisible = stat.graphShouldShowNumberOfSessions } } else -> { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt index 3ef35761..029a16aa 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt @@ -80,7 +80,7 @@ interface Displayable : Localizable { interface Localizable { /** - * The resource identifier of the localized titleResId + * The resource uniqueIdentifier of the localized titleResId */ val resId: Int? get() { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index 3aed7f8c..fc50cc90 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -258,7 +258,7 @@ enum class RowViewType(private var layoutRes: Int) { } if (row is StatRow) { - itemView.findViewById(R.id.nextArrow)?.isVisible = row.stat.hasEvolutionGraph + itemView.findViewById(R.id.nextArrow)?.isVisible = row.stat.hasProgressGraph } // Listener diff --git a/app/src/main/java/net/pokeranalytics/android/util/enumerations/SavableEnumeration.kt b/app/src/main/java/net/pokeranalytics/android/util/enumerations/SavableEnumeration.kt new file mode 100644 index 00000000..d6ba4c99 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/util/enumerations/SavableEnumeration.kt @@ -0,0 +1,23 @@ +package net.pokeranalytics.android.util.enumerations + +import net.pokeranalytics.android.exceptions.EnumIdentifierNotFoundException +import net.pokeranalytics.android.exceptions.MisconfiguredSavableEnumException + +interface IntSearchable { + + fun valuesInternal(): Array + + fun valueByIdentifier(identifier: Int) : T { + val values = this.valuesInternal().filter { it.uniqueIdentifier == identifier } + return when (values.size) { + 0 -> throw EnumIdentifierNotFoundException("Savable enumeration uniqueIdentifier $identifier not found") + 1 -> values.first() + else -> throw MisconfiguredSavableEnumException("Savable enumeration has multiple elements with uniqueIdentifier $identifier") + } + } + +} + +interface IntIdentifiable { + var uniqueIdentifier: Int +} \ No newline at end of file diff --git a/app/src/main/res/menu/navigation_home.xml b/app/src/main/res/menu/navigation_home.xml index b401e5d9..f8b8501e 100644 --- a/app/src/main/res/menu/navigation_home.xml +++ b/app/src/main/res/menu/navigation_home.xml @@ -10,7 +10,7 @@ + android:titleResId="@string/statIds" />