Merge branch 'dev' of gitlab.com:stax-river/poker-analytics into dev

feature/top10
Aurelien Hubert 7 years ago
commit ebb31da9d5
  1. 74
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  2. 4
      app/src/main/java/net/pokeranalytics/android/calculus/Report.kt
  3. 38
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  4. 10
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  5. 12
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt

@ -8,8 +8,11 @@ import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.filter.name
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.SessionSet
import net.pokeranalytics.android.util.extensions.startOfDay
import timber.log.Timber
import java.util.*
import kotlin.math.max
import kotlin.math.min
/**
* The class performing stats computation
@ -62,7 +65,6 @@ class Calculator {
return true
}
// var aggregation: Aggregation? = null
}
companion object {
@ -161,7 +163,12 @@ class Calculator {
val computables = computableGroup.computables(realm)
Timber.d(">>>> Start computing group ${computableGroup.name}, ${computables.size} computables")
val results: ComputedResults = ComputedResults(computableGroup)
val computeLongestStreak = options.displayedStats.contains(LONGEST_STREAKS)
if (computeLongestStreak) {
computables.sort("session.startDate")
}
val results = ComputedResults(computableGroup)
val sum = computables.sum(ComputableResult.Field.RATED_NET.identifier).toDouble()
val totalHands = computables.sum(ComputableResult.Field.ESTIMATED_HANDS.identifier).toDouble()
@ -170,10 +177,18 @@ class Calculator {
val winningSessionCount = computables.sum(ComputableResult.Field.IS_POSITIVE.identifier).toInt()
val totalBuyin = computables.sum(ComputableResult.Field.RATED_BUYIN.identifier).toDouble()
// Compute for each session
val maxNetResult = computables.max(ComputableResult.Field.RATED_NET.identifier)?.toDouble()
val minNetResult = computables.min(ComputableResult.Field.RATED_NET.identifier)?.toDouble()
when (options.evolutionValues) {
Options.EvolutionValues.STANDARD -> {
if (options.displayedStats.contains(LOCATIONS_PLAYED)) {
results.addStat(LOCATIONS_PLAYED, computables.distinctBy { it.session?.location?.id }.size.toDouble())
}
val shouldIterateOverComputables =
(options.evolutionValues == Options.EvolutionValues.STANDARD || computeLongestStreak)
// Iterate for each session
if (shouldIterateOverComputables) {
var index: Int = 0
var tSum = 0.0
@ -182,6 +197,7 @@ class Calculator {
var tWinningSessionCount = 0
var tBuyinSum = 0.0
var tHands = 0.0
var winStreak = 0; var loseStreak = 0; var currentStreak = 0
computables.forEach { computable ->
index++
@ -192,6 +208,23 @@ class Calculator {
tBuyinSum += computable.ratedBuyin
tHands += computable.estimatedHands
if (computable.isPositive == 1) {
if (currentStreak >= 0) {
currentStreak++
} else {
currentStreak = 1
loseStreak = min(loseStreak, currentStreak)
}
}
if (computable.isPositive == 0) {
if (currentStreak <= 0) {
currentStreak--
} else {
currentStreak = -1
winStreak = max(winStreak, currentStreak)
}
}
val session =
computable.session ?: throw IllegalStateException("Computing lone ComputableResult")
results.addEvolutionValue(tSum, stat = NETRESULT, data = session)
@ -214,10 +247,15 @@ class Calculator {
}
}
if (currentStreak >= 0) {
winStreak = max(winStreak, currentStreak)
} else {
loseStreak = min(loseStreak, currentStreak)
}
else -> {
// nothing
}
results.addStat(LONGEST_STREAKS, winStreak.toDouble(), loseStreak.toDouble())
}
val sessionSets = computableGroup.sessionSets(realm)
@ -228,6 +266,7 @@ class Calculator {
val gSum = sessionSets.sum(SessionSet.Field.RATED_NET.identifier).toDouble()
val gTotalHands = sessionSets.sum(SessionSet.Field.ESTIMATED_HANDS.identifier).toDouble()
val gBBSum = sessionSets.sum(SessionSet.Field.BB_NET.identifier).toDouble()
val maxDuration = sessionSets.max(SessionSet.Field.NET_DURATION.identifier)?.toDouble()
val hourlyRate = gSum / gHourlyDuration
// var bbHourlyRate = gBBSum / gDuration
@ -242,6 +281,7 @@ class Calculator {
var tBBSum = 0.0
var tHourlyRate = 0.0
var tHourlyRateBB = 0.0
val daysSet = mutableSetOf<Date>()
sessionSets.forEach { sessionSet ->
tIndex++
@ -252,6 +292,7 @@ class Calculator {
tHourlyRate = gSum / tHourlyDuration
tHourlyRateBB = gBBSum / tHourlyDuration
daysSet.add(sessionSet.startDate.startOfDay())
when (options.evolutionValues) {
Options.EvolutionValues.STANDARD -> {
@ -308,6 +349,8 @@ class Calculator {
}
}
results.addStat(DAYS_PLAYED, daysSet.size.toDouble())
}
}
else -> {
@ -350,15 +393,24 @@ class Calculator {
ComputedStat(HOURLY_RATE_BB, gBBSum / gHourlyDuration),
ComputedStat(AVERAGE_NET_BB, gBBSum / bbSessionCount),
ComputedStat(HANDS_PLAYED, totalHands)
)
)
Stat.returnOnInvestment(sum, totalBuyin)?.let { roi ->
results.addStats(setOf(ComputedStat(ROI, roi)))
results.addStat(ROI, roi)
}
Stat.netBBPer100Hands(bbSum, totalHands)?.let { netBB100 ->
results.addStats(setOf(ComputedStat(NET_BB_PER_100_HANDS, netBB100)))
results.addStat(NET_BB_PER_100_HANDS, netBB100)
}
maxNetResult?.let { max ->
results.addStat(MAXIMUM_NETRESULT, max)
}
minNetResult?.let { min ->
results.addStat(MINIMUM_NETRESULT, min)
}
maxDuration?.let { maxd ->
results.addStat(MAXIMUM_DURATION, maxd)
}
val bbPer100Hands = bbSum / totalHands * 100

@ -182,6 +182,10 @@ class ComputedResults(group: ComputableGroup) {
}
}
fun addStat(stat: Stat, value: Double, secondValue: Double? = null) {
this._computedStats[stat] = ComputedStat(stat, value, secondValue)
}
fun addStats(computedStats: Set<ComputedStat>) {
computedStats.forEach {
this._computedStats[it.stat] = it

@ -70,7 +70,14 @@ enum class Stat : RowRepresentable {
STANDARD_DEVIATION,
STANDARD_DEVIATION_HOURLY,
STANDARD_DEVIATION_BB_PER_100_HANDS,
HANDS_PLAYED;
HANDS_PLAYED,
LOCATIONS_PLAYED,
LONGEST_STREAKS,
MAXIMUM_NETRESULT,
MINIMUM_NETRESULT,
MAXIMUM_DURATION,
DAYS_PLAYED
;
/**
* Returns whether the stat evolution numericValues requires a distribution sorting
@ -120,6 +127,12 @@ enum class Stat : RowRepresentable {
STANDARD_DEVIATION_HOURLY -> R.string.standard_deviation_per_hour
STANDARD_DEVIATION_BB_PER_100_HANDS -> R.string.standard_deviation_bb_per_100_hands
HANDS_PLAYED -> R.string.number_of_hands
LOCATIONS_PLAYED -> R.string.locations_played
LONGEST_STREAKS -> R.string.longest_streaks
MAXIMUM_NETRESULT -> R.string.max_net_result
MINIMUM_NETRESULT -> R.string.min_net_result
MAXIMUM_DURATION -> R.string.longest_session
DAYS_PLAYED -> R.string.days_played
}
}
@ -127,7 +140,7 @@ enum class Stat : RowRepresentable {
/**
* Formats the value of the stat to be suitable for display
*/
fun format(value: Double, currency: Currency? = null, context: Context): TextFormat {
fun format(value: Double, secondValue: Double? = null, currency: Currency? = null, context: Context): TextFormat {
if (value.isNaN()) {
return TextFormat(NULL_TEXT, R.color.white)
@ -135,30 +148,33 @@ enum class Stat : RowRepresentable {
when (this) {
// Amounts + red/green
Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE -> {
NETRESULT, HOURLY_RATE, AVERAGE, MAXIMUM_NETRESULT, MINIMUM_NETRESULT -> {
val color = if (value >= this.threshold) R.color.green else R.color.red
return TextFormat(value.toCurrency(currency), color)
}
// Red/green numericValues
Stat.HOURLY_RATE_BB, Stat.AVERAGE_NET_BB, Stat.NET_BB_PER_100_HANDS -> {
HOURLY_RATE_BB, AVERAGE_NET_BB, NET_BB_PER_100_HANDS -> {
val color = if (value >= this.threshold) R.color.green else R.color.red
return TextFormat(value.formatted(), color)
}
// white integers
Stat.NUMBER_OF_SETS, Stat.NUMBER_OF_GAMES, Stat.HANDS_PLAYED -> {
NUMBER_OF_SETS, NUMBER_OF_GAMES, HANDS_PLAYED, LOCATIONS_PLAYED, DAYS_PLAYED -> {
return TextFormat("${value.toInt()}")
} // white durations
Stat.DURATION, Stat.AVERAGE_DURATION -> {
DURATION, AVERAGE_DURATION, MAXIMUM_DURATION -> {
return TextFormat(value.formattedHourlyDuration())
} // red/green percentages
Stat.WIN_RATIO, Stat.ROI -> {
WIN_RATIO, ROI -> {
val color = if (value * 100 >= this.threshold) R.color.green else R.color.red
return TextFormat("${(value * 100).formatted()}%", color)
} // white amountsr
Stat.AVERAGE_BUYIN, Stat.STANDARD_DEVIATION, Stat.STANDARD_DEVIATION_HOURLY,
Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> {
AVERAGE_BUYIN, STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY,
STANDARD_DEVIATION_BB_PER_100_HANDS -> {
return TextFormat(value.toCurrency(currency))
}
LONGEST_STREAKS -> {
return TextFormat("${value.toInt()}W / ${secondValue!!.toInt()}L")
}
else -> throw FormattingException("Stat formatting of ${this.name} not handled")
}
}
@ -228,7 +244,7 @@ enum class Stat : RowRepresentable {
/**
* ComputedStat contains a [stat] and their associated [value]
*/
class ComputedStat(var stat: Stat, var value: Double, var currency: Currency? = null) {
class ComputedStat(var stat: Stat, var value: Double, var secondValue: Double? = null, var currency: Currency? = null) {
constructor(stat: Stat, value: Double, previousValue: Double?) : this(stat, value) {
if (previousValue != null) {
@ -245,7 +261,7 @@ class ComputedStat(var stat: Stat, var value: Double, var currency: Currency? =
* Formats the value of the stat to be suitable for display
*/
fun format(context: Context): TextFormat {
return this.stat.format(this.value, this.currency, context)
return this.stat.format(this.value, this.secondValue, this.currency, context)
}
}

@ -581,7 +581,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = getFormattedDuration(),
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency)
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency = currency)
)
)
rows.add(SeparatorRow())
@ -591,7 +591,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
resId = R.string.pause,
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency)
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency = currency)
)
)
rows.add(SeparatorRow())
@ -601,14 +601,14 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = getFormattedDuration(),
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency)
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0, currency = currency)
)
)
rows.add(
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT,
resId = R.string.hour_rate_without_pauses,
computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate, currency)
computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate, currency = currency)
)
)
@ -896,7 +896,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
}
value?.let {
return stat.format(it, currency, context)
return stat.format(it, currency = currency, context = context)
} ?: run {
return TextFormat(NULL_TEXT)
}

@ -88,15 +88,15 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
override fun formattedValue(stat: Stat, context: Context) : TextFormat {
return when (stat) {
Stat.NETRESULT, Stat.AVERAGE -> stat.format(this.ratedNet, null, context)
Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), null, context)
Stat.HOURLY_RATE -> stat.format(this.hourlyRate, null, context)
Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, null, context)
Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, null, context)
Stat.NETRESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null, context = context)
Stat.DURATION, Stat.AVERAGE_DURATION -> stat.format(this.netDuration.toDouble(), currency = null, context = context)
Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null, context = context)
Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null, context = context)
Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, currency = null, context = context)
Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> {
val netBBPer100Hands = Stat.netBBPer100Hands(this.bbNet, this.estimatedHands)
if (netBBPer100Hands != null) {
return stat.format(this.estimatedHands, null, context)
return stat.format(this.estimatedHands, currency = null, context = context)
} else {
return TextFormat(NULL_TEXT)
}

@ -198,7 +198,7 @@ class GraphFragment : PokerAnalyticsFragment(), OnChartValueSelectedListener, Co
val formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(it.startDate())
val entryValue = it.formattedValue(this.stat, requireContext())
val totalStatValue = this.stat.format(e.y.toDouble(), null, requireContext())
val totalStatValue = this.stat.format(e.y.toDouble(), currency = null, context = requireContext())
this.legendView.setItemData(this.stat, formattedDate, entryValue, totalStatValue)
}

Loading…
Cancel
Save