Add custom report launching + Savable enums + Tests

dev
Laurent 7 years ago
parent 3c26986a41
commit c78ede97fa
  1. 18
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  2. 16
      app/src/main/java/net/pokeranalytics/android/calculus/Report.kt
  3. 104
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  4. 5
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  5. 61
      app/src/main/java/net/pokeranalytics/android/model/Criteria.kt
  6. 4
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  7. 2
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt
  8. 8
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  9. 56
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  10. 5
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  11. 9
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  12. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  13. 2
      app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt
  14. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt
  15. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt
  16. 77
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  17. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt
  18. 5
      app/src/main/java/net/pokeranalytics/android/ui/graph/GraphUnderlyingEntry.kt
  19. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/LegendView.kt
  20. 2
      app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
  21. 2
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  22. 23
      app/src/main/java/net/pokeranalytics/android/util/enumerations/SavableEnumeration.kt
  23. 2
      app/src/main/res/menu/navigation_home.xml
  24. 21
      app/src/test/java/net/pokeranalytics/android/SavableEnumTest.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<ComputableGroup>, 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 {

@ -97,9 +97,9 @@ class Report(var options: Calculator.Options) {
*/
class ComputableGroup(query: Query, stats: List<Stat>? = null) {
// constructor(query: Query, stats: List<Stat>? = null) : this(query.name, query.conditions)
// constructor(query: Query, statIds: List<Stat>? = null) : this(query.name, query.conditions)
//
// private constructor(name: String = "", conditions: List<QueryCondition> = listOf(), stats: List<Stat>? = null)
// private constructor(name: String = "", conditions: List<QueryCondition> = listOf(), statIds: List<Stat>? = null)
var query: Query = query
@ -165,7 +165,7 @@ class ComputableGroup(query: Query, stats: List<Stat>? = null) {
}
/**
* The list of stats to display
* The list of statIds to display
*/
var stats: List<Stat>? = stats
@ -175,7 +175,7 @@ class ComputableGroup(query: Query, stats: List<Stat>? = 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<Stat>? = 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<Stat, ComputedStat> = mutableMapOf()
// The map containing all evolution numericValues for all stats
// The map containing all evolution numericValues for all statIds
private var _evolutionValues: MutableMap<Stat, MutableList<Point>> = 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) {

@ -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<out Timed>) {
}
/**
* 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<Stat> {
override fun valuesInternal(): Array<Stat> {
return values()
}
val userSelectableList: List<Stat>
get() {
@ -65,7 +66,7 @@ enum class Stat : RowRepresentable {
val evolutionValuesList: List<Stat>
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<AggregationType>
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,

@ -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<String>) : 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}")
}
}

@ -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<Criteria>.combined(): List<Query> {
val comparatorList = ArrayList<List<Query>>()
@ -54,21 +56,21 @@ fun getCombinations(queries: List<List<Query>>): List<Query> {
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 <reified T : NameManageable> comparison(): List<Query> {
return compare<QueryCondition.QueryDataCondition<NameManageable>, T>()
}
}
abstract class SimpleCriteria(private val conditions: List<QueryCondition>) : Criteria() {
abstract class SimpleCriteria(private val conditions: List<QueryCondition>, uniqueIdentifier: Int) : Criteria(uniqueIdentifier) {
fun comparison(): List<Query> {
return conditions.map { Query(it) }
}
}
abstract class ListCriteria : Criteria() {
abstract class ListCriteria(uniqueIdentifier: Int) : Criteria(uniqueIdentifier) {
inline fun <reified T : QueryCondition.ListOfValues<S>, reified S : Comparable<S>> comparison(): List<Query> {
QueryCondition.distinct<Session, T, S>()?.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<Query>
get() {
@ -225,7 +227,8 @@ sealed class Criteria : RowRepresentable {
}
}
companion object {
companion object : IntSearchable<Criteria> {
inline fun <reified S : QueryCondition.QueryDataCondition<NameManageable>, reified T : NameManageable> compare(): List<Query> {
val objects = mutableListOf<S>()
val realm = Realm.getDefaultInstance()
@ -263,7 +266,11 @@ sealed class Criteria : RowRepresentable {
)
}
// SavableEnum
override fun valuesInternal(): Array<Criteria> {
return all.toTypedArray()
}
}
}

@ -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
}

@ -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 {

@ -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++
}
}

@ -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<Int> = RealmList()
/**
* An optional list of criteriaIds to compare statIds
*/
var criteriaIds: RealmList<Int> = 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<Int> = listOf()
// var stats: List<Int> = listOf()
@Ignore
override val viewType: Int = RowViewType.TITLE_ARROW.ordinal
// The filters associated with the report
var filters: RealmList<Filter> = 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
)
}
}

@ -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")
}
}

@ -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

@ -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 -> {

@ -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

@ -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

@ -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()
}
}
}

@ -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<ReportSetup>
private var adapterRows = mutableListOf<RowRepresentable>()
companion object {
@ -47,19 +54,12 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour
return fragment
}
val rowRepresentation: List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
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<RowRepresentable>? {
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<RowRepresentable>? {
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()

@ -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 {

@ -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<out Timed>) {
}
interface GraphUnderlyingEntry {
val entryTitle: String

@ -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 -> {

@ -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() {

@ -258,7 +258,7 @@ enum class RowViewType(private var layoutRes: Int) {
}
if (row is StatRow) {
itemView.findViewById<AppCompatImageView?>(R.id.nextArrow)?.isVisible = row.stat.hasEvolutionGraph
itemView.findViewById<AppCompatImageView?>(R.id.nextArrow)?.isVisible = row.stat.hasProgressGraph
}
// Listener

@ -0,0 +1,23 @@
package net.pokeranalytics.android.util.enumerations
import net.pokeranalytics.android.exceptions.EnumIdentifierNotFoundException
import net.pokeranalytics.android.exceptions.MisconfiguredSavableEnumException
interface IntSearchable<T : IntIdentifiable> {
fun valuesInternal(): Array<T>
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
}

@ -10,7 +10,7 @@
<item
android:id="@+id/navigation_stats"
android:icon="@drawable/ic_outline_chart"
android:titleResId="@string/stats" />
android:titleResId="@string/statIds" />
<item
android:id="@+id/navigation_settings"

@ -0,0 +1,21 @@
package net.pokeranalytics.android
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria
import org.junit.Assert
import org.junit.Test
class SavableEnumTest {
@Test
fun testSavableEnumConfiguration() {
val statIds = Stat.valuesInternal().map { it.uniqueIdentifier }
Assert.assertEquals(statIds.toSet().size, statIds.size)
val criteriaIds = Criteria.valuesInternal().map { it.uniqueIdentifier }
Assert.assertEquals(criteriaIds.toSet().size, criteriaIds.size)
}
}
Loading…
Cancel
Save