RealmWriteService + FlatTimeInterval for better performance

threading
Laurent 3 years ago
parent 526e50f8e4
commit 929365fc4c
  1. 4
      app/src/main/java/net/pokeranalytics/android/RealmWriteService.kt
  2. 262
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  3. 5
      app/src/main/java/net/pokeranalytics/android/calculus/ComputableGroup.kt
  4. 16
      app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt
  6. 7
      app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt
  7. 13
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  8. 61
      app/src/main/java/net/pokeranalytics/android/model/realm/FlatTimeInterval.kt
  9. 25
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  10. 247
      app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt
  11. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt
  12. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  13. 8
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt
  14. 27
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt
  15. 26
      app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt

@ -43,9 +43,9 @@ class RealmWriteService : Service() {
this.realm.executeTransactionAsync({ asyncRealm -> this.realm.executeTransactionAsync({ asyncRealm ->
handler(asyncRealm) handler(asyncRealm)
Timber.d(">> handler done") // Timber.d(">> handler done")
}, { }, {
Timber.d(">> YEAAAAAAAAAAAH !!!") // Timber.d(">> YEAAAAAAAAAAAH !!!")
this.realm.refresh() this.realm.refresh()
}, { }, {
Timber.d(">> NOOOOO error = $it") Timber.d(">> NOOOOO error = $it")

@ -6,10 +6,10 @@ import net.pokeranalytics.android.calculus.Stat.*
import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.combined import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.model.extensions.hourlyDuration
import net.pokeranalytics.android.model.filter.Query import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.filter import net.pokeranalytics.android.model.filter.filter
import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.utils.SessionInterval
import net.pokeranalytics.android.util.extensions.startOfDay import net.pokeranalytics.android.util.extensions.startOfDay
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -45,8 +45,18 @@ class Calculator {
filter: Filter? = null, filter: Filter? = null,
aggregationType: AggregationType? = null, aggregationType: AggregationType? = null,
userGenerated: Boolean = false, userGenerated: Boolean = false,
reportSetupId: String? = null) : reportSetupId: String? = null
this(progressValues, stats, criterias, filter?.query ?: Query(), filter?.id, aggregationType, userGenerated, reportSetupId) ) :
this(
progressValues,
stats,
criterias,
filter?.query ?: Query(),
filter?.id,
aggregationType,
userGenerated,
reportSetupId
)
/** /**
* Specifies whether progress values should be added and their kind * Specifies whether progress values should be added and their kind
@ -111,6 +121,7 @@ class Calculator {
get() { get() {
return this.stats.contains(LOCATIONS_PLAYED) return this.stats.contains(LOCATIONS_PLAYED)
} }
/** /**
* Whether the number of days played should be computed * Whether the number of days played should be computed
*/ */
@ -118,6 +129,7 @@ class Calculator {
get() { get() {
return this.stats.contains(DAYS_PLAYED) return this.stats.contains(DAYS_PLAYED)
} }
/** /**
* Whether progress values should be managed at the group level * Whether progress values should be managed at the group level
*/ */
@ -167,7 +179,11 @@ class Calculator {
} }
return when (aggregationType) { return when (aggregationType) {
AggregationType.SESSION, AggregationType.DURATION -> this.computeGroups(realm, listOf(group), options) AggregationType.SESSION, AggregationType.DURATION -> this.computeGroups(
realm,
listOf(group),
options
)
AggregationType.MONTH, AggregationType.YEAR -> { AggregationType.MONTH, AggregationType.YEAR -> {
this.computeStats(realm, options) this.computeStats(realm, options)
} }
@ -198,7 +214,11 @@ class Calculator {
/** /**
* Computes all statIds for list of Session sessionGroup * Computes all statIds for list of Session sessionGroup
*/ */
fun computeGroups(realm: Realm, groups: List<ComputableGroup>, options: Options = Options()): Report { fun computeGroups(
realm: Realm,
groups: List<ComputableGroup>,
options: Options = Options()
): Report {
val report = Report(options) val report = Report(options)
for (group in groups) { for (group in groups) {
@ -236,9 +256,14 @@ class Calculator {
/** /**
* Computes statIds for a SessionSet * Computes statIds for a SessionSet
*/ */
fun compute(realm: Realm, computableGroup: ComputableGroup, options: Options = Options()): ComputedResults { fun compute(
realm: Realm,
computableGroup: ComputableGroup,
options: Options = Options()
): ComputedResults {
val results = ComputedResults(computableGroup, options.shouldManageMultiGroupProgressValues) val results =
ComputedResults(computableGroup, options.shouldManageMultiGroupProgressValues)
val computables = computableGroup.computables(realm, options.shouldSortValues) val computables = computableGroup.computables(realm, options.shouldSortValues)
@ -252,32 +277,39 @@ class Calculator {
// Timber.d("$$$ buyin = ${it.ratedBuyin} $$$ net result = ${it.ratedNet}") // Timber.d("$$$ buyin = ${it.ratedBuyin} $$$ net result = ${it.ratedNet}")
// } // }
val d1 = Date()
var ratedNet = computables.sum(ComputableResult.Field.RATED_NET.identifier).toDouble() var ratedNet = computables.sum(ComputableResult.Field.RATED_NET.identifier).toDouble()
if (options.includedTransactions.isNotEmpty()) { if (options.includedTransactions.isNotEmpty()) {
for (transactionType in options.includedTransactions) { for (transactionType in options.includedTransactions) {
val transactions = computableGroup.transactions(realm, transactionType, options.shouldSortValues) val transactions = computableGroup.transactions(
val transactionRatedAmount = transactions.sum(Transaction.Field.RATED_AMOUNT.identifier).toDouble() realm,
transactionType,
options.shouldSortValues
)
val transactionRatedAmount =
transactions.sum(Transaction.Field.RATED_AMOUNT.identifier).toDouble()
ratedNet += transactionRatedAmount ratedNet += transactionRatedAmount
} }
} }
results.addStat(NET_RESULT, ratedNet) results.addStat(NET_RESULT, ratedNet)
val totalHands = computables.sum(ComputableResult.Field.ESTIMATED_HANDS.identifier).toDouble() val totalHands =
computables.sum(ComputableResult.Field.ESTIMATED_HANDS.identifier).toDouble()
results.addStat(HANDS_PLAYED, totalHands) results.addStat(HANDS_PLAYED, totalHands)
val bbSum = computables.sum(ComputableResult.Field.BB_NET.identifier).toDouble() val bbSum = computables.sum(ComputableResult.Field.BB_NET.identifier).toDouble()
results.addStat(BB_NET_RESULT, bbSum) results.addStat(BB_NET_RESULT, bbSum)
val bbSessionCount = computables.sum(ComputableResult.Field.HAS_BIG_BLIND.identifier).toInt() val bbSessionCount =
computables.sum(ComputableResult.Field.HAS_BIG_BLIND.identifier).toInt()
results.addStat(BB_SESSION_COUNT, bbSessionCount.toDouble()) results.addStat(BB_SESSION_COUNT, bbSessionCount.toDouble())
val winningSessionCount = computables.sum(ComputableResult.Field.IS_POSITIVE.identifier).toInt() val winningSessionCount =
computables.sum(ComputableResult.Field.IS_POSITIVE.identifier).toInt()
results.addStat(WINNING_SESSION_COUNT, winningSessionCount.toDouble()) results.addStat(WINNING_SESSION_COUNT, winningSessionCount.toDouble())
val totalBuyin = computables.sum(ComputableResult.Field.RATED_BUYIN.identifier).toDouble() val totalBuyin =
computables.sum(ComputableResult.Field.RATED_BUYIN.identifier).toDouble()
results.addStat(TOTAL_BUYIN, totalBuyin) results.addStat(TOTAL_BUYIN, totalBuyin)
val totalTips = computables.sum(ComputableResult.Field.RATED_TIPS.identifier).toDouble() val totalTips = computables.sum(ComputableResult.Field.RATED_TIPS.identifier).toDouble()
@ -285,12 +317,14 @@ class Calculator {
// Timber.d("########## totalBuyin = ${totalBuyin} ### sum = ${sum}") // Timber.d("########## totalBuyin = ${totalBuyin} ### sum = ${sum}")
val maxNetResult = computables.max(ComputableResult.Field.RATED_NET.identifier)?.toDouble() val maxNetResult =
computables.max(ComputableResult.Field.RATED_NET.identifier)?.toDouble()
maxNetResult?.let { maxNetResult?.let {
results.addStat(MAXIMUM_NET_RESULT, it) results.addStat(MAXIMUM_NET_RESULT, it)
} }
val minNetResult = computables.min(ComputableResult.Field.RATED_NET.identifier)?.toDouble() val minNetResult =
computables.min(ComputableResult.Field.RATED_NET.identifier)?.toDouble()
minNetResult?.let { minNetResult?.let {
results.addStat(MINIMUM_NET_RESULT, it) results.addStat(MINIMUM_NET_RESULT, it)
} }
@ -310,7 +344,10 @@ class Calculator {
// } // }
if (options.computeLocationsPlayed) { if (options.computeLocationsPlayed) {
results.addStat(LOCATIONS_PLAYED, computables.distinctBy { it.session?.location?.id }.size.toDouble()) results.addStat(
LOCATIONS_PLAYED,
computables.distinctBy { it.session?.location?.id }.size.toDouble()
)
} }
var average = 0.0 // also used for standard deviation later var average = 0.0 // also used for standard deviation later
@ -334,7 +371,6 @@ class Calculator {
averageBB = bbSum / bbSessionCount averageBB = bbSum / bbSessionCount
results.addStat(AVERAGE_NET_BB, averageBB) results.addStat(AVERAGE_NET_BB, averageBB)
} }
val d2 = Date()
val shouldIterateOverComputables = val shouldIterateOverComputables =
(options.progressValues == Options.ProgressValues.STANDARD || options.computeLongestStreak) (options.progressValues == Options.ProgressValues.STANDARD || options.computeLongestStreak)
@ -381,11 +417,20 @@ class Calculator {
} }
val session = val session =
computable.session ?: throw PAIllegalStateException("Computing lone ComputableResult") computable.session
?: throw PAIllegalStateException("Computing lone ComputableResult")
results.addEvolutionValue(tSum, stat = NET_RESULT, data = session) results.addEvolutionValue(tSum, stat = NET_RESULT, data = session)
results.addEvolutionValue(tSum / index, stat = AVERAGE, data = session) results.addEvolutionValue(tSum / index, stat = AVERAGE, data = session)
results.addEvolutionValue(index.toDouble(), stat = NUMBER_OF_GAMES, data = session) results.addEvolutionValue(
results.addEvolutionValue(tBBSum / tBBSessionCount, stat = AVERAGE_NET_BB, data = session) index.toDouble(),
stat = NUMBER_OF_GAMES,
data = session
)
results.addEvolutionValue(
tBBSum / tBBSessionCount,
stat = AVERAGE_NET_BB,
data = session
)
results.addEvolutionValue( results.addEvolutionValue(
(tWinningSessionCount.toDouble() / index.toDouble()), (tWinningSessionCount.toDouble() / index.toDouble()),
stat = WIN_RATIO, stat = WIN_RATIO,
@ -394,12 +439,25 @@ class Calculator {
results.addEvolutionValue( results.addEvolutionValue(
tITMCount.toDouble() / index.toDouble(), tITMCount.toDouble() / index.toDouble(),
stat = TOURNAMENT_ITM_RATIO, stat = TOURNAMENT_ITM_RATIO,
data = session) data = session
results.addEvolutionValue(tBuyinSum / index, stat = AVERAGE_BUYIN, data = session) )
results.addEvolutionValue(computable.ratedNet, stat = STANDARD_DEVIATION, data = session) results.addEvolutionValue(
tBuyinSum / index,
stat = AVERAGE_BUYIN,
data = session
)
results.addEvolutionValue(
computable.ratedNet,
stat = STANDARD_DEVIATION,
data = session
)
Stat.netBBPer100Hands(tBBSum, tHands)?.let { netBB100 -> Stat.netBBPer100Hands(tBBSum, tHands)?.let { netBB100 ->
results.addEvolutionValue(netBB100, stat = NET_BB_PER_100_HANDS, data = session) results.addEvolutionValue(
netBB100,
stat = NET_BB_PER_100_HANDS,
data = session
)
} }
Stat.returnOnInvestment(tSum, tBuyinSum)?.let { roi -> Stat.returnOnInvestment(tSum, tBuyinSum)?.let { roi ->
@ -415,32 +473,34 @@ class Calculator {
} }
// loseStreak is negative and we want it positive // loseStreak is negative and we want it positive
results.addStat(LONGEST_STREAKS, longestWinStreak.toDouble(), -longestLoseStreak.toDouble()) results.addStat(
LONGEST_STREAKS,
longestWinStreak.toDouble(),
-longestLoseStreak.toDouble()
)
} }
val d3 = Date()
val sessionSets = computableGroup.sessionSets(realm, options.shouldSortValues) val sessionSets = computableGroup.sessionSets(realm, options.shouldSortValues)
results.addStat(NUMBER_OF_SETS, sessionSets.size.toDouble()) results.addStat(NUMBER_OF_SETS, sessionSets.size.toDouble())
var gHourlyDuration: Double? = null // var gHourlyDuration: Double? = null
var gBBSum: Double? = null //// var gBBSum: Double? = null
var maxDuration: Double? = null var maxDuration: Double? = null
if (computableGroup.conditions.isEmpty()) { // SessionSets are fine if (computableGroup.conditions.isEmpty()) { // SessionSets are fine
gHourlyDuration = // gHourlyDuration =
sessionSets.sum(SessionSet.Field.NET_DURATION.identifier).toDouble() / 3600000 // (milliseconds to hours) // sessionSets.sum(SessionSet.Field.NET_DURATION.identifier).toDouble() / 3600000 // (milliseconds to hours)
gBBSum = sessionSets.sum(SessionSet.Field.BB_NET.identifier).toDouble() // gBBSum = sessionSets.sum(SessionSet.Field.BB_NET.identifier).toDouble()
sessionSets.max(SessionSet.Field.NET_DURATION.identifier)?.let { sessionSets.max(SessionSet.Field.NET_DURATION.identifier)?.let {
maxDuration = it.toDouble() / 3600000 maxDuration = it.toDouble() / 3600000
} }
} }
val shouldIterateOverSets = computableGroup.conditions.isNotEmpty() val shouldIterateOverSets = options.progressValues != Options.ProgressValues.NONE
|| options.progressValues != Options.ProgressValues.NONE
|| options.computeDaysPlayed || options.computeDaysPlayed
// || computableGroup.conditions.isNotEmpty()
// Session Set // Session Set
if (shouldIterateOverSets) { if (shouldIterateOverSets) {
@ -464,7 +524,7 @@ class Calculator {
tBBSum += setStats.bbSum tBBSum += setStats.bbSum
tHourlyDuration += setStats.hourlyDuration tHourlyDuration += setStats.hourlyDuration
tTotalHands += setStats.estimatedHands tTotalHands += setStats.estimatedHands
tMaxDuration = max(tMaxDuration, setStats.hourlyDuration) tMaxDuration = max(tMaxDuration, setStats.hourlyDuration.toDouble())
tHourlyRate = tRatedNetSum / tHourlyDuration tHourlyRate = tRatedNetSum / tHourlyDuration
tHourlyRateBB = tBBSum / tHourlyDuration tHourlyRateBB = tBBSum / tHourlyDuration
@ -472,8 +532,16 @@ class Calculator {
when (options.progressValues) { when (options.progressValues) {
Options.ProgressValues.STANDARD -> { Options.ProgressValues.STANDARD -> {
results.addEvolutionValue(tHourlyRate, stat = HOURLY_RATE, data = sessionSet) results.addEvolutionValue(
results.addEvolutionValue(tIndex.toDouble(), stat = NUMBER_OF_SETS, data = sessionSet) tHourlyRate,
stat = HOURLY_RATE,
data = sessionSet
)
results.addEvolutionValue(
tIndex.toDouble(),
stat = NUMBER_OF_SETS,
data = sessionSet
)
results.addEvolutionValue( results.addEvolutionValue(
sessionSet.hourlyDuration, sessionSet.hourlyDuration,
tHourlyDuration, tHourlyDuration,
@ -485,12 +553,26 @@ class Calculator {
stat = AVERAGE_HOURLY_DURATION, stat = AVERAGE_HOURLY_DURATION,
data = sessionSet data = sessionSet
) )
results.addEvolutionValue(tHourlyRateBB, stat = HOURLY_RATE_BB, data = sessionSet) results.addEvolutionValue(
tHourlyRateBB,
stat = HOURLY_RATE_BB,
data = sessionSet
)
} }
Options.ProgressValues.TIMED -> { Options.ProgressValues.TIMED -> {
results.addEvolutionValue(tRatedNetSum, tHourlyDuration, NET_RESULT, sessionSet) results.addEvolutionValue(
results.addEvolutionValue(tHourlyRate, tHourlyDuration, HOURLY_RATE, sessionSet) tRatedNetSum,
tHourlyDuration,
NET_RESULT,
sessionSet
)
results.addEvolutionValue(
tHourlyRate,
tHourlyDuration,
HOURLY_RATE,
sessionSet
)
results.addEvolutionValue( results.addEvolutionValue(
tIndex.toDouble(), tIndex.toDouble(),
tHourlyDuration, tHourlyDuration,
@ -509,7 +591,12 @@ class Calculator {
AVERAGE_HOURLY_DURATION, AVERAGE_HOURLY_DURATION,
sessionSet sessionSet
) )
results.addEvolutionValue(tHourlyRateBB, tHourlyDuration, HOURLY_RATE_BB, sessionSet) results.addEvolutionValue(
tHourlyRateBB,
tHourlyDuration,
HOURLY_RATE_BB,
sessionSet
)
Stat.netBBPer100Hands(tBBSum, tTotalHands)?.let { netBB100 -> Stat.netBBPer100Hands(tBBSum, tTotalHands)?.let { netBB100 ->
results.addEvolutionValue( results.addEvolutionValue(
@ -529,32 +616,44 @@ class Calculator {
} }
gHourlyDuration = tHourlyDuration // gHourlyDuration = tHourlyDuration
gBBSum = tBBSum // gBBSum = tBBSum
maxDuration = tMaxDuration maxDuration = tMaxDuration
} }
val d4 = Date() // var hourlyRate = 0.0
// if (gHourlyDuration != null) {
//
// hourlyRate = ratedNet / gHourlyDuration
// if (sessionSets.size > 0) {
// val avgDuration = gHourlyDuration / sessionSets.size
// results.addStat(HOURLY_RATE, hourlyRate)
// results.addStat(AVERAGE_HOURLY_DURATION, avgDuration)
// }
// results.addStat(HOURLY_DURATION, gHourlyDuration)
// }
val timeIntervals = computableGroup.timeIntervals(realm)
val duration = timeIntervals.sum("duration").toDouble()
val breakDuration = sessionSets.sum("breakDuration").toDouble()
var hourlyRate = 0.0 val netHourlyDuration = (duration - breakDuration) / 3600000
if (gHourlyDuration != null) { val hourlyRate = ratedNet / netHourlyDuration
hourlyRate = ratedNet / gHourlyDuration
if (sessionSets.size > 0) {
val avgDuration = gHourlyDuration / sessionSets.size
results.addStat(HOURLY_RATE, hourlyRate) results.addStat(HOURLY_RATE, hourlyRate)
results.addStat(AVERAGE_HOURLY_DURATION, avgDuration) results.addStat(AVERAGE_HOURLY_DURATION, netHourlyDuration / sessionSets.size)
} results.addStat(HOURLY_DURATION, netHourlyDuration)
results.addStat(HOURLY_DURATION, gHourlyDuration)
}
if (gBBSum != null) { results.addStat(HOURLY_RATE_BB, bbSum / netHourlyDuration)
if (gHourlyDuration != null) { results.addStat(AVERAGE_NET_BB, bbSum / bbSessionCount)
results.addStat(HOURLY_RATE_BB, gBBSum / gHourlyDuration)
} // if (gBBSum != null) {
results.addStat(AVERAGE_NET_BB, gBBSum / bbSessionCount) // if (gHourlyDuration != null) {
} // results.addStat(HOURLY_RATE_BB, gBBSum / gHourlyDuration)
// }
// results.addStat(AVERAGE_NET_BB, gBBSum / bbSessionCount)
// }
maxDuration?.let { maxd -> maxDuration?.let { maxd ->
results.addStat(MAXIMUM_DURATION, maxd) // (milliseconds to hours) results.addStat(MAXIMUM_DURATION, maxd) // (milliseconds to hours)
@ -568,45 +667,31 @@ class Calculator {
// Session // Session
var stdSum = 0.0 var stdSum = 0.0
var stdBBSum = 0.0 var stdBBSum = 0.0
var stdBBper100HandsSum = 0.0 var stdBBPer100HandsSum = 0.0
for (computable in computables) { for (computable in computables) {
stdSum += (computable.ratedNet - average).pow(2.0) stdSum += (computable.ratedNet - average).pow(2.0)
stdBBSum += (computable.bbNet - averageBB).pow(2.0) stdBBSum += (computable.bbNet - averageBB).pow(2.0)
stdBBper100HandsSum += (computable.bbPer100Hands - bbPer100Hands).pow(2.0) stdBBPer100HandsSum += (computable.bbPer100Hands - bbPer100Hands).pow(2.0)
} }
val standardDeviation = sqrt(stdSum / computables.size) val standardDeviation = sqrt(stdSum / computables.size)
val standardDeviationBB = sqrt(stdBBSum / computables.size) val standardDeviationBB = sqrt(stdBBSum / computables.size)
val standardDeviationBBper100Hands = sqrt(stdBBper100HandsSum / computables.size) val standardDeviationBBper100Hands = sqrt(stdBBPer100HandsSum / computables.size)
results.addStat(STANDARD_DEVIATION, standardDeviation) results.addStat(STANDARD_DEVIATION, standardDeviation)
results.addStat(STANDARD_DEVIATION_BB, standardDeviationBB) results.addStat(STANDARD_DEVIATION_BB, standardDeviationBB)
results.addStat(STANDARD_DEVIATION_BB_PER_100_HANDS, standardDeviationBBper100Hands) results.addStat(STANDARD_DEVIATION_BB_PER_100_HANDS, standardDeviationBBper100Hands)
// Session Set // Session Set
if (gHourlyDuration != null) {
var hourlyStdSum = 0.0 var hourlyStdSum = 0.0
for (set in sessionSets) { for (set in sessionSets) {
val ssStats = SSStats(set, computableGroup.query) val ssStats = SSStats(set, computableGroup.query)
val sHourlyRate = ssStats.hourlyRate val setHourlyRate = ssStats.hourlyRate
hourlyStdSum += (sHourlyRate - hourlyRate).pow(2.0) hourlyStdSum += (setHourlyRate - hourlyRate).pow(2.0)
} }
val hourlyStandardDeviation = sqrt(hourlyStdSum / sessionSets.size) val hourlyStandardDeviation = sqrt(hourlyStdSum / sessionSets.size)
results.addStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation) results.addStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation)
} }
}
val d5 = Date()
val s1 = d2.time - d1.time
val s2 = d3.time - d2.time
val s3 = d4.time - d3.time
val s4 = d5.time - d4.time
Timber.d("Section 1 = $s1")
Timber.d("Section 2 = $s2")
Timber.d("Section 3 = $s3")
Timber.d("Section 4 = $s4")
return results return results
} }
@ -616,14 +701,18 @@ class Calculator {
class SSStats(sessionSet: SessionSet, query: Query) { // Session Set Stats class SSStats(sessionSet: SessionSet, query: Query) { // Session Set Stats
var hourlyDuration: Double = 0.0 var hourlyDuration: Long = 0L
var estimatedHands: Double = 0.0 var estimatedHands: Double = 0.0
var bbSum: Double = 0.0 var bbSum: Double = 0.0
var ratedNet: Double = 0.0 var ratedNet: Double = 0.0
val hourlyRate: Double val hourlyRate: Double
get() { get() {
return this.ratedNet / this.hourlyDuration return if (this.hourlyDuration > 0L) {
this.ratedNet / this.hourlyDuration.toDouble()
} else {
0.0
}
} }
init { init {
@ -636,10 +725,15 @@ class SSStats(sessionSet: SessionSet, query: Query) { // Session Set Stats
if (setSessions.size == filteredSessions.size) { if (setSessions.size == filteredSessions.size) {
this.initStatsWithSet(sessionSet) this.initStatsWithSet(sessionSet)
} else { } else {
ratedNet = filteredSessions.sumOf { it.computableResult?.ratedNet ?: 0.0 } ratedNet = filteredSessions.sumOf { it.computableResult?.ratedNet ?: 0.0 }
bbSum = filteredSessions.sumOf { it.bbNet } bbSum = filteredSessions.sumOf { it.bbNet }
hourlyDuration = filteredSessions.hourlyDuration
estimatedHands = filteredSessions.sumOf { it.estimatedHands } estimatedHands = filteredSessions.sumOf { it.estimatedHands }
val intervals = SessionInterval.intervalMap(filteredSessions.toSet())
val netDuration = intervals.sumOf { it.duration }
val estimatedBreak = if (netDuration > 0.0) sessionSet.breakDuration * sessionSet.netDuration / netDuration else 0L
hourlyDuration = netDuration - estimatedBreak
} }
} }
} }
@ -647,7 +741,7 @@ class SSStats(sessionSet: SessionSet, query: Query) { // Session Set Stats
private fun initStatsWithSet(sessionSet: SessionSet) { private fun initStatsWithSet(sessionSet: SessionSet) {
ratedNet = sessionSet.ratedNet ratedNet = sessionSet.ratedNet
bbSum = sessionSet.bbNet bbSum = sessionSet.bbNet
hourlyDuration = sessionSet.hourlyDuration hourlyDuration = sessionSet.hourlyDuration.toLong()
estimatedHands = sessionSet.estimatedHands estimatedHands = sessionSet.estimatedHands
} }

@ -64,6 +64,11 @@ class ComputableGroup(val query: Query, var displayedStats: List<Stat>? = null)
return computables return computables
} }
fun timeIntervals(realm: Realm): RealmResults<FlatTimeInterval> {
return Filter.queryOn(realm, this.query)
}
/** /**
* The list of sets to compute * The list of sets to compute
*/ */

@ -55,18 +55,18 @@ class ReportWhistleBlower(var context: Context) {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
this.sessions = realm.where(Session::class.java).findAll() sessions = realm.where(Session::class.java).findAll()
this.sessions?.addChangeListener { _ -> sessions?.addChangeListener { _ ->
requestReportLaunch() requestReportLaunch()
} }
this.results = realm.where(Result::class.java).findAll() results = realm.where(Result::class.java).findAll()
this.results?.addChangeListener { _ -> results?.addChangeListener { _ ->
requestReportLaunch() requestReportLaunch()
} }
this.sessionSets = realm.where(SessionSet::class.java).findAll() sessionSets = realm.where(SessionSet::class.java).findAll()
this.sessionSets?.addChangeListener { _ -> sessionSets?.addChangeListener { _ ->
requestReportLaunch() requestReportLaunch()
} }
@ -156,13 +156,13 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
} }
fun cancel() { fun cancel() {
Timber.d("Reportwhistleblower task CANCEL") // Timber.d("Reportwhistleblower task CANCEL")
this.cancelled = true this.cancelled = true
} }
private fun launchReports() { private fun launchReports() {
Timber.d("====== Report whistleblower launch batch...") // Timber.d("====== Report whistleblower launch batch...")
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {

@ -133,7 +133,7 @@ val AbstractList<Session>.hourlyDuration: Double
return intervals.sumOf { it.hourlyDuration } return intervals.sumOf { it.hourlyDuration }
} }
class TimeInterval(var start: Date, var end: Date, var breakDuration: Long) { class TimeInterval(var start: Date, var end: Date, var breakDuration: Long = 0L) {
val hourlyDuration: Double val hourlyDuration: Double
get() { get() {

@ -32,9 +32,9 @@ import net.pokeranalytics.android.util.CrashLogging
* *
*/ */
class UnmanagedFilterField(message: String) : Exception(message) { //class UnmanagedFilterField(message: String) : Exception(message) {
//
} //}
/** /**
* Companion-level Interface to indicate an RealmObject class can be filtered and to provide all the fieldNames (eg: parameter's path) needed to be query on. * Companion-level Interface to indicate an RealmObject class can be filtered and to provide all the fieldNames (eg: parameter's path) needed to be query on.
@ -64,6 +64,7 @@ class FilterHelper {
SessionSet::class.java -> SessionSet.fieldNameForQueryType(queryCondition) SessionSet::class.java -> SessionSet.fieldNameForQueryType(queryCondition)
Transaction::class.java -> Transaction.fieldNameForQueryType(queryCondition) Transaction::class.java -> Transaction.fieldNameForQueryType(queryCondition)
Result::class.java -> Result.fieldNameForQueryType(queryCondition) Result::class.java -> Result.fieldNameForQueryType(queryCondition)
FlatTimeInterval::class.java -> FlatTimeInterval.fieldNameForQueryType(queryCondition)
else -> { else -> {
CrashLogging.logException(PAIllegalStateException("Filterable type fields are not defined for condition ${queryCondition::class}, class ${T::class}")) CrashLogging.logException(PAIllegalStateException("Filterable type fields are not defined for condition ${queryCondition::class}, class ${T::class}"))
null null

@ -3,6 +3,7 @@ package net.pokeranalytics.android.model.migrations
import io.realm.DynamicRealm import io.realm.DynamicRealm
import io.realm.RealmMigration import io.realm.RealmMigration
import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.FlatTimeInterval
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -341,6 +342,18 @@ class PokerAnalyticsMigration : RealmMigration {
crs.addField("id", String::class.java).setRequired("id", true) crs.addField("id", String::class.java).setRequired("id", true)
crs.addPrimaryKey("id") crs.addPrimaryKey("id")
} }
schema.create("FlatTimeInterval")?.let { fs ->
fs.addField("id", String::class.java).setRequired("id", true)
fs.addPrimaryKey("id")
fs.addField("startDate", Date::class.java).setRequired("startDate", true)
fs.addField("endDate", Date::class.java).setRequired("endDate", true)
fs.addField("duration", Long::class.java)
schema.get("Session")?.let { ss ->
ss.addRealmSetField("flatTimeIntervals", fs)
}
}
currentVersion++ currentVersion++
} }

@ -0,0 +1,61 @@
package net.pokeranalytics.android.model.realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmClass
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
import java.util.*
@RealmClass
open class FlatTimeInterval : RealmObject(), Filterable {
@PrimaryKey
var id = UUID.randomUUID().toString()
/**
* The start date of the session
*/
var startDate: Date = Date()
set(value) {
field = value
this.computeDuration()
}
/**
* The start date of the session
*/
var endDate: Date = Date()
set(value) {
field = value
this.computeDuration()
}
/**
* the net duration of the session, automatically calculated
*/
var duration: Long = 0L
@LinkingObjects("flatTimeIntervals")
val sessions: RealmResults<Session>? = null
private fun computeDuration() {
duration = endDate.time - startDate.time
}
companion object {
fun fieldNameForQueryType(queryCondition: Class <out QueryCondition>): String? {
Session.fieldNameForQueryType(queryCondition)?.let {
return "sessions.$it"
}
return null
}
}
}

@ -33,10 +33,7 @@ import net.pokeranalytics.android.ui.graph.Graph
import net.pokeranalytics.android.ui.view.* import net.pokeranalytics.android.ui.view.*
import net.pokeranalytics.android.ui.view.rows.SessionPropertiesRow import net.pokeranalytics.android.ui.view.rows.SessionPropertiesRow
import net.pokeranalytics.android.util.* import net.pokeranalytics.android.util.*
import net.pokeranalytics.android.util.extensions.hourMinute import net.pokeranalytics.android.util.extensions.*
import net.pokeranalytics.android.util.extensions.shortDateTime
import net.pokeranalytics.android.util.extensions.toCurrency
import net.pokeranalytics.android.util.extensions.toMinutes
import java.text.DateFormat import java.text.DateFormat
import java.text.NumberFormat import java.text.NumberFormat
import java.text.ParseException import java.text.ParseException
@ -202,6 +199,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
*/ */
var startDate: Date? = null var startDate: Date? = null
set(value) { set(value) {
val previous = this.startDate
field = value field = value
if (value == null) { if (value == null) {
startDateHourMinuteComponent = null startDateHourMinuteComponent = null
@ -217,7 +215,9 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
if (value != null && this.endDate != null && value.after(this.endDate)) { if (value != null && this.endDate != null && value.after(this.endDate)) {
this.endDate = null this.endDate = null
} }
this.dateChanged()
SessionSetManager.startChanged(this, min(previous, value))
// this.computeStats() // this.computeStats()
} }
@ -227,6 +227,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
@Index @Index
var endDate: Date? = null var endDate: Date? = null
set(value) { set(value) {
val previous = this.endDate
field = value field = value
if (value == null) { if (value == null) {
endDateHourMinuteComponent = null endDateHourMinuteComponent = null
@ -237,7 +238,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
} }
this.computeNetDuration() this.computeNetDuration()
this.dateChanged() SessionSetManager.endChanged(this, max(previous, value))
this.defineDefaultTournamentBuyinIfNecessary() this.defineDefaultTournamentBuyinIfNecessary()
// this.computeStats() // this.computeStats()
} }
@ -373,6 +374,9 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
// The custom fields values // The custom fields values
var customFieldEntries: RealmList<CustomFieldEntry> = RealmList() var customFieldEntries: RealmList<CustomFieldEntry> = RealmList()
// The list of opponents who participated to the session
var flatTimeIntervals: RealmList<FlatTimeInterval> = RealmList()
// The number of hands played during the sessions // The number of hands played during the sessions
var handsCount: Int? = null var handsCount: Int? = null
@ -380,10 +384,6 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.generateStakes() this.generateStakes()
} }
private fun dateChanged() {
SessionSetManager.sessionDateChanged(this)
}
// /** // /**
// * Manages impacts on SessionSets // * Manages impacts on SessionSets
// * Should be called when the start / end date are changed // * Should be called when the start / end date are changed
@ -697,10 +697,8 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
CrashLogging.log("Deletes session. Id = ${this.id}") CrashLogging.log("Deletes session. Id = ${this.id}")
if (isValid) { if (isValid) {
// realm.executeTransaction {
cleanup() cleanup()
deleteFromRealm() deleteFromRealm()
// }
} else { } else {
CrashLogging.log("Attempt to delete an invalid session") CrashLogging.log("Attempt to delete an invalid session")
} }
@ -715,6 +713,9 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.sessionSet?.let { this.sessionSet?.let {
SessionSetManager.removeFromTimeline(this) SessionSetManager.removeFromTimeline(this)
} }
SessionSetManager.sessionDateChanged(this)
// cleanup unnecessary related objects // cleanup unnecessary related objects
this.result?.deleteFromRealm() this.result?.deleteFromRealm()
this.computableResult?.deleteFromRealm() this.computableResult?.deleteFromRealm()

@ -1,13 +1,18 @@
package net.pokeranalytics.android.model.utils package net.pokeranalytics.android.model.utils
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.RealmResults import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.ModelException import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.model.realm.FlatTimeInterval
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
import net.pokeranalytics.android.util.extensions.findById import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.max
import net.pokeranalytics.android.util.extensions.min
import timber.log.Timber import timber.log.Timber
import java.util.*
class CorruptSessionSetException(message: String) : Exception(message) class CorruptSessionSetException(message: String) : Exception(message)
@ -17,12 +22,25 @@ class CorruptSessionSetException(message: String) : Exception(message)
*/ */
object SessionSetManager { object SessionSetManager {
var sessions: RealmResults<Session> var sessions: RealmResults<Session>? = null
private val sessionIdsToProcess = mutableSetOf<String>() private val sessionIdsToProcess = mutableSetOf<String>()
private var start: Date? = null
private var end: Date? = null
fun configure() {} // launch init fun configure() {} // launch init
fun startChanged(session: Session, date: Date?) {
this.start = min(this.start, date)
this.sessionIdsToProcess.add(session.id)
}
fun endChanged(session: Session, date: Date?) {
this.end = max(this.end, date)
this.sessionIdsToProcess.add(session.id)
}
fun sessionDateChanged(session: Session) { fun sessionDateChanged(session: Session) {
this.sessionIdsToProcess.add(session.id) this.sessionIdsToProcess.add(session.id)
} }
@ -31,11 +49,12 @@ object SessionSetManager {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
this.sessions = realm.where(Session::class.java).findAllAsync() sessions = realm.where(Session::class.java).findAllAsync()
this.sessions.addChangeListener { _, _ -> sessions?.addChangeListener { _, _ ->
if (this.sessionIdsToProcess.isNotEmpty()) { if (this.start != null && this.end != null) {
realm.executeTransactionAsync { asyncRealm -> realm.executeTransactionAsync { asyncRealm ->
processSessions(asyncRealm) processSessions(asyncRealm)
cleanUp()
} }
} }
} }
@ -43,21 +62,38 @@ object SessionSetManager {
realm.close() realm.close()
} }
private fun cleanUp() {
this.start = null
this.end = null
// this.sessionIdsToProcess.clear()
}
private fun processSessions(realm: Realm) { private fun processSessions(realm: Realm) {
Timber.d("***** processSessions, process count = ${sessionIdsToProcess.size}") // Timber.d("***** processSessions, process count = ${sessionIdsToProcess.size}")
for (sessionId in this.sessionIdsToProcess) { val start = this.start
realm.findById<Session>(sessionId)?.let { session -> val end = this.end
if (session.startDate != null && session.endDate != null) {
val sessions = sessionIdsToProcess.mapNotNull { realm.findById<Session>(it) }
for (session in sessions) {
// Session Sets
val startDate = session.startDate
val endDate = session.endDate
if (startDate != null && endDate != null) {
updateTimeline(session) updateTimeline(session)
} else if (session.sessionSet != null) { } else if (session.sessionSet != null) {
removeFromTimeline(session) removeFromTimeline(session)
} }
} }
// FlatTimeIntervals
if (start != null && end != null) {
processFlatTimeInterval(realm, start, end)
} }
this.sessionIdsToProcess.clear()
} }
/** /**
@ -76,25 +112,37 @@ object SessionSetManager {
throw ModelException("End date should never be null here") throw ModelException("End date should never be null here")
} }
val sessionSets = this.matchingSets(session) val start = session.startDate!!
cleanupSessionSets(session, sessionSets) val end = session.endDate!!
// val sessionId = session.id val sessionSets = this.matchingData<SessionSet>(session.realm, start, end)
// realm.executeTransactionAsync { asyncRealm -> cleanupSessionSets(session, sessionSets)
// asyncRealm.findById<Session>(sessionId)?.let { s ->
// val sessionSets = this.matchingSets(session)
// cleanupSessionSets(session, sessionSets)
// }
// }
} }
private fun matchingSets(session: Session): RealmResults<SessionSet> { // private fun matchingSets(session: Session): RealmResults<SessionSet> {
val realm = session.realm // val realm = session.realm
val endDate = session.endDate!! // tested above // val endDate = session.endDate!! // tested above
val startDate = session.startDate!! // val startDate = session.startDate!!
//
// val query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java)
//
// query
// .lessThanOrEqualTo("startDate", startDate)
// .greaterThanOrEqualTo("endDate", startDate)
// .or()
// .lessThanOrEqualTo("startDate", endDate)
// .greaterThanOrEqualTo("endDate", endDate)
// .or()
// .greaterThanOrEqualTo("startDate", startDate)
// .lessThanOrEqualTo("endDate", endDate)
//
// return query.findAll()
// }
val query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java) private inline fun <reified T : RealmModel> matchingData(realm: Realm, startDate: Date, endDate: Date): RealmResults<T> {
val query: RealmQuery<T> = realm.where(T::class.java)
query query
.lessThanOrEqualTo("startDate", startDate) .lessThanOrEqualTo("startDate", startDate)
@ -127,7 +175,7 @@ object SessionSetManager {
sessionSets.deleteAllFromRealm() sessionSets.deleteAllFromRealm()
allImpactedSessions.forEach { impactedSession -> allImpactedSessions.forEach { impactedSession ->
val sets = matchingSets(impactedSession) val sets = matchingData<SessionSet>(impactedSession.realm, impactedSession.startDate!!, impactedSession.endDate!!)
this.updateTimeFrames(sets, impactedSession) this.updateTimeFrames(sets, impactedSession)
} }
@ -249,4 +297,155 @@ object SessionSetManager {
} }
} }
private fun processFlatTimeInterval(realm: Realm, start: Date, end: Date) {
val sessions = matchingData<Session>(realm, start, end)
val intervalsStore = IntervalsStore(sessions.toSet())
intervalsStore.intervals.forEach { it.deleteFromRealm() }
val intervals = SessionInterval.intervalMap(intervalsStore.sessions)
for (interval in intervals) {
val sortedDates = interval.dates.sorted()
for (i in (0 until sortedDates.size - 1)) {
val s = sortedDates[i]
val e = sortedDates[i + 1]
val matchingSessions = interval.sessions.filter {
val sd = it.startDate
val ed = it.endDate
(sd != null && ed != null && sd <= s && ed >= e)
}
if (matchingSessions.isNotEmpty()) {
Timber.d("**** Create FTI: $s - $e")
val fti = FlatTimeInterval()
fti.startDate = s
fti.endDate = e
matchingSessions.forEach { it.flatTimeIntervals.add(fti) }
realm.insertOrUpdate(fti)
} else {
Timber.w("The FTI has no sessions")
}
}
}
}
}
class IntervalsStore(sessions: Set<Session>) {
var start: Date = Date()
var end: Date = Date(0L)
val intervals = mutableSetOf<FlatTimeInterval>()
val sessions = mutableSetOf<Session>()
private val sessionIds: MutableSet<String> = mutableSetOf()
init {
processSessions(sessions)
}
private fun processSessions(sessions: Set<Session>) {
this.sessions.addAll(sessions)
for (session in sessions) {
loadIntervals(session)
}
}
private fun loadIntervals(session: Session) {
if (sessionIds.contains(session.id)) {
return
}
session.startDate?.let { this.start = min(this.start, it) }
session.endDate?.let { this.end = max(this.end, it) }
this.sessionIds.add(session.id)
for (fti in session.flatTimeIntervals) {
this.intervals.add(fti)
fti.sessions?.let { sessions ->
for (s in sessions) {
loadIntervals(s)
}
}
}
}
}
class SessionInterval(session: Session) {
var start: Date
var end: Date?
var sessions: MutableSet<Session> = mutableSetOf()
val dates: MutableSet<Date> = mutableSetOf()
val duration: Long
get() {
val endDate = end ?: Date()
return endDate.time - start.time
}
init {
this.start = session.startDate!!
this.end = session.endDate
this.addSession(session)
}
private fun addSession(session: Session) {
this.sessions.add(session)
session.startDate?.let { this.dates.add(it) }
session.endDate?.let { endDate ->
this.dates.add(endDate)
if (endDate > end) {
end = endDate
}
}
}
companion object {
fun intervalMap(sessions: Set<Session>): List<SessionInterval> {
val sorted = sessions.sortedBy { it.startDate }
val intervals = mutableListOf<SessionInterval>()
sorted.firstOrNull()?.let { firstSession ->
var currentInterval = SessionInterval(firstSession)
intervals.add(currentInterval)
for (session in sessions.drop(1)) {
val start = session.startDate!!
val currentEnd = currentInterval.end
if (currentEnd != null && start > currentEnd) {
val interval = SessionInterval(session)
currentInterval = interval
intervals.add(interval)
} else {
currentInterval.addSession(session)
}
}
}
intervals.forEach {
Timber.d("s = ${it.start}, e = ${it.end}")
}
return intervals
}
}
} }

@ -167,7 +167,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
val async = async { val async = async {
val s = Date() val s = Date()
Timber.d(">>> start...") // Timber.d(">>> start...")
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
realm.refresh() realm.refresh()
@ -199,7 +199,6 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
Timber.d(">>> Launch statistics computations") Timber.d(">>> Launch statistics computations")
val filter: Filter? = this.currentFilter(this.requireContext(), realm)?.let { val filter: Filter? = this.currentFilter(this.requireContext(), realm)?.let {
if (it.filterableType == currentFilterable) { it } else { null } if (it.filterableType == currentFilterable) { it } else { null }
} }

@ -27,6 +27,7 @@ import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.StatRow import net.pokeranalytics.android.ui.view.rows.StatRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.TextFormat import net.pokeranalytics.android.util.TextFormat
import timber.log.Timber
open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentableDataSource, open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentableDataSource,
RowRepresentableDelegate { RowRepresentableDelegate {

@ -388,7 +388,7 @@ class CalendarFragment : RealmFragment(), StaticRowRepresentableDataSource,
val async = async { val async = async {
val s = Date() val s = Date()
Timber.d(">>> start...") // Timber.d(">>> start...")
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
realm.refresh() realm.refresh()
@ -436,6 +436,8 @@ class CalendarFragment : RealmFragment(), StaticRowRepresentableDataSource,
private fun launchStatComputation(realm: Realm) { private fun launchStatComputation(realm: Realm) {
return
Timber.d(">>> Launch calendar computations") Timber.d(">>> Launch calendar computations")
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
@ -660,8 +662,8 @@ class CalendarFragment : RealmFragment(), StaticRowRepresentableDataSource,
} }
} }
Timber.d("Display data: ${System.currentTimeMillis() - startDate.time}ms") // Timber.d("Display data: ${System.currentTimeMillis() - startDate.time}ms")
Timber.d("Rows: ${rows.size}") // Timber.d("Rows: ${rows.size}")
this.calendarAdapter.notifyDataSetChanged() this.calendarAdapter.notifyDataSetChanged()

@ -19,9 +19,7 @@ import net.pokeranalytics.android.api.BlogPostApi
import net.pokeranalytics.android.databinding.FragmentFeedBinding import net.pokeranalytics.android.databinding.FragmentFeedBinding
import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.activity.BillingActivity import net.pokeranalytics.android.ui.activity.BillingActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.activity.components.RequestCode
@ -87,10 +85,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis
override fun asyncListenedEntityChange(realm: Realm, clazz: Class<out RealmModel>) { override fun asyncListenedEntityChange(realm: Realm, clazz: Class<out RealmModel>) {
Timber.d("asyncListenedEntityChange for $clazz") // Timber.d("asyncListenedEntityChange for $clazz")
when (clazz.kotlin) { when (clazz.kotlin) {
Session::class -> { Session::class -> {
Timber.d("WOWOWOOWOOWOWOWOWOWOWOWOWO") // Timber.d("WOWOWOOWOOWOWOWOWOWOWOWOWO")
this.sessionAdapter.refreshData() this.sessionAdapter.refreshData()
this.sessionAdapter.notifyDataSetChanged() this.sessionAdapter.notifyDataSetChanged()
} }
@ -286,9 +284,20 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis
displayBlogPostButton() displayBlogPostButton()
binding.postButton.setOnClickListener { binding.postButton.setOnClickListener {
Preferences.setBlogTipsTapped(requireContext())
parentActivity?.openUrl(URL.BLOG_TIPS.value) getRealm().executeTransactionAsync { realm ->
displayBlogPostButton()
realm.where<Session>().findAll().deleteAllFromRealm()
realm.where<SessionSet>().findAll().deleteAllFromRealm()
realm.where<FlatTimeInterval>().findAll().deleteAllFromRealm()
realm.where<Result>().findAll().deleteAllFromRealm()
realm.where<ComputableResult>().findAll().deleteAllFromRealm()
}
// Preferences.setBlogTipsTapped(requireContext())
// parentActivity?.openUrl(URL.BLOG_TIPS.value)
// displayBlogPostButton()
} }
binding.postButton.viewTreeObserver.addOnGlobalLayoutListener { binding.postButton.viewTreeObserver.addOnGlobalLayoutListener {
@ -632,7 +641,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis
show = true show = true
this.badgeDrawable?.number = newCount this.badgeDrawable?.number = newCount
} }
this.binding.postButton.isVisible = show this.binding.postButton.isVisible = true
this.badgeDrawable?.isVisible = show this.badgeDrawable?.isVisible = show
} }

@ -7,6 +7,32 @@ import java.util.*
// Calendar // Calendar
fun min(d1: Date, d2: Date): Date {
return if (d1 < d2) d1 else d2
}
fun max(d1: Date, d2: Date): Date {
return if (d1 > d2) d1 else d2
}
@JvmName("min1")
fun min(d1: Date?, d2: Date?): Date? {
return if (d1 != null) {
if (d2 != null) min(d1, d2) else d1
} else {
d2
}
}
@JvmName("max1")
fun max(d1: Date?, d2: Date?): Date? {
return if (d1 != null) {
if (d2 != null) max(d1, d2) else d1
} else {
d2
}
}
// Return a double representing the hour / minute of a date from a calendar // Return a double representing the hour / minute of a date from a calendar
fun Calendar.hourMinute(): Double { fun Calendar.hourMinute(): Double {
return (this.get(Calendar.HOUR_OF_DAY) + this.get(Calendar.MINUTE).toDouble() / 60.0).roundOffDecimal() return (this.get(Calendar.HOUR_OF_DAY) + this.get(Calendar.MINUTE).toDouble() / 60.0).roundOffDecimal()

Loading…
Cancel
Save