Change the frequency of backups to 10 hours after the last change

kmmtest
Laurent 2 years ago
parent b5769173b7
commit 4583b5e12a
  1. 10
      app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt
  2. 10
      app/src/main/java/net/pokeranalytics/android/calculus/optimalduration/CashGameOptimalDurationCalculator.kt
  3. 1
      app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt
  4. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  7. 8
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/EditableDataActivity.kt
  8. 8
      app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionActivity.kt
  9. 78
      app/src/main/java/net/pokeranalytics/android/util/BackupOperator.kt
  10. 75
      app/src/main/java/net/pokeranalytics/android/util/BackupTask.kt
  11. 41
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionTransactionCSVDescriptor.kt

@ -62,7 +62,7 @@ class ReportWhistleBlower(var context: Context) {
} }
fun requestReportLaunch() { fun requestReportLaunch() {
Timber.d(">>> Launch report") // Timber.d(">>> Launch report")
if (paused) { if (paused) {
return return
@ -171,7 +171,7 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
private fun launchReport(realm: Realm, report: StaticReport) { private fun launchReport(realm: Realm, report: StaticReport) {
Timber.d(">>> launch report = $report") // Timber.d(">>> launch report = $report")
when (report) { when (report) {
StaticReport.OptimalDuration -> launchOptimalDuration(realm, report) StaticReport.OptimalDuration -> launchOptimalDuration(realm, report)
@ -202,7 +202,7 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
for (stat in result.options.stats) { for (stat in result.options.stats) {
Timber.d("analyse stat: $stat for report: $staticReport") // Timber.d("analyse stat: $stat for report: $staticReport")
// Get current performance // Get current performance
var query = performancesQuery(realm, staticReport, stat) var query = performancesQuery(realm, staticReport, stat)
@ -261,10 +261,10 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
} }
} ?: run { // if there is no max but a now irrelevant Performance, we delete it } ?: run { // if there is no max but a now irrelevant Performance, we delete it
Timber.d("NO best computed value, current perf = $currentPerf ") // Timber.d("NO best computed value, current perf = $currentPerf ")
currentPerf?.let { perf -> currentPerf?.let { perf ->
realm.executeTransaction { realm.executeTransaction {
Timber.d("Delete perf: stat = ${perf.stat}, report = ${perf.reportId}") // Timber.d("Delete perf: stat = ${perf.stat}, report = ${perf.reportId}")
perf.deleteFromRealm() perf.deleteFromRealm()
} }
} }

@ -65,7 +65,7 @@ class CashGameOptimalDurationCalculator {
var validBuckets = 0 var validBuckets = 0
val hkeys = sessionsByDuration.keys.map { it / 3600 / 1000.0 }.sorted() val hkeys = sessionsByDuration.keys.map { it / 3600 / 1000.0 }.sorted()
Timber.d("Stop notif > keys: $hkeys ") // Timber.d("Stop notif > keys: $hkeys ")
for (key in sessionsByDuration.keys.sorted()) { for (key in sessionsByDuration.keys.sorted()) {
val sessionCount = sessionsByDuration[key]?.size ?: 0 val sessionCount = sessionsByDuration[key]?.size ?: 0
if (start == null && sessionCount >= minimumValidityCount) { if (start == null && sessionCount >= minimumValidityCount) {
@ -76,15 +76,15 @@ class CashGameOptimalDurationCalculator {
validBuckets++ validBuckets++
} }
} }
Timber.d("Stop notif > validBuckets: $validBuckets ") // Timber.d("Stop notif > validBuckets: $validBuckets ")
if (!(start != null && end != null && (end - start) >= intervalValidity)) { if (!(start != null && end != null && (end - start) >= intervalValidity)) {
Timber.d("Stop notif > invalid setup: $start / $end ") // Timber.d("Stop notif > invalid setup: $start / $end ")
return null return null
} }
// define if we have enough sessions // define if we have enough sessions
if (sessions.size < 50) { if (sessions.size < 50) {
Timber.d("Stop notif > not enough sessions: ${sessions.size} ") // Timber.d("Stop notif > not enough sessions: ${sessions.size} ")
return null return null
} }
@ -134,7 +134,7 @@ class CashGameOptimalDurationCalculator {
return bestDuration return bestDuration
} }
Timber.d("Stop notif > not found, best duration: $bestDuration") // Timber.d("Stop notif > not found, best duration: $bestDuration")
realm.close() realm.close()
return null return null
} }

@ -120,7 +120,6 @@ fun Session.scheduleStopNotification(context: Context, optimalDuration: Long) {
.addTag(this.id) .addTag(this.id)
.build() .build()
// WorkManager.getInstance(context).enqueue(work)
WorkManager.getInstance(context).enqueueUniqueWork(this.id, ExistingWorkPolicy.REPLACE, work) WorkManager.getInstance(context).enqueueUniqueWork(this.id, ExistingWorkPolicy.REPLACE, work)
} }

@ -162,7 +162,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
val async = GlobalScope.async { val async = GlobalScope.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()
@ -174,7 +174,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
val e = Date() val e = Date()
val duration = (e.time - s.time) / 1000.0 val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in $duration seconds") // Timber.d(">>> ended in $duration seconds")
} }
async.await() async.await()
@ -241,7 +241,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
val tSessionGroup = ComputableGroup(Query(QueryCondition.IsTournament).merge(query), tStats) val tSessionGroup = ComputableGroup(Query(QueryCondition.IsTournament).merge(query), tStats)
Timber.d(">>>>> Start computations...") // Timber.d(">>>>> Start computations...")
val options = Calculator.Options() val options = Calculator.Options()
val computedStats = mutableListOf<Stat>() val computedStats = mutableListOf<Stat>()

@ -208,7 +208,7 @@ open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentab
var report: Report? = null var report: Report? = null
val test = GlobalScope.async { val test = GlobalScope.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()

@ -166,7 +166,7 @@ class ProgressReportFragment : AbstractReportFragment() {
GlobalScope.launch { GlobalScope.launch {
val s = Date() val s = Date()
Timber.d(">>> start...") // Timber.d(">>> start...")
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()

@ -51,10 +51,10 @@ class EditableDataActivity : MediaActivity() {
initUI() initUI()
} }
override fun onPause() { // override fun onPause() {
super.onPause() // super.onPause()
this.paApplication.backupOperator?.backupIfNecessary() // this.paApplication.backupOperator?.backupIfNecessary()
} // }
/** /**
* Init UI * Init UI

@ -68,10 +68,10 @@ class SessionActivity: BaseActivity() {
initUI() initUI()
} }
override fun onPause() { // override fun onPause() {
super.onPause() // super.onPause()
this.paApplication.backupOperator?.backupIfNecessary() // this.paApplication.backupOperator?.backupIfNecessary()
} // }
override fun onBackPressed() { override fun onBackPressed() {
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)

@ -1,26 +1,25 @@
package net.pokeranalytics.android.util package net.pokeranalytics.android.util
import android.content.Context import android.content.Context
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import io.realm.Realm import io.realm.Realm
import io.realm.RealmResults import io.realm.RealmResults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.pokeranalytics.android.api.BackupApi
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.util.csv.ProductCSVDescriptors import net.pokeranalytics.android.util.csv.DataType
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.concurrent.TimeUnit
class BackupOperator(var context: Context) { class BackupOperator(var context: Context) {
private var sessions: RealmResults<Session>? = null private var sessions: RealmResults<Session>? = null
private var transactions: RealmResults<Transaction>? = null private var transactions: RealmResults<Transaction>? = null
private var sessionsChanged = false private var sessionsInitialized = false
private var transactionsChanged = false private var transactionsInitialized = false
private val realm = Realm.getDefaultInstance() private val realm = Realm.getDefaultInstance()
@ -28,59 +27,40 @@ class BackupOperator(var context: Context) {
this.sessions = this.realm.where(Session::class.java).findAllAsync() this.sessions = this.realm.where(Session::class.java).findAllAsync()
this.sessions?.addChangeListener { _ -> this.sessions?.addChangeListener { _ ->
sessionsChanged = true if (this.sessionsInitialized) {
Preferences.getBackupEmail(context)?.let {
backupDataType(DataType.SESSION)
}
}
this.sessionsInitialized = true
} }
this.transactions = this.realm.where(Transaction::class.java).findAllAsync() this.transactions = this.realm.where(Transaction::class.java).findAllAsync()
this.transactions?.addChangeListener { _ -> this.transactions?.addChangeListener { _ ->
transactionsChanged = true if (this.transactionsInitialized) {
} Preferences.getBackupEmail(context)?.let {
backupDataType(DataType.TRANSACTION)
}
fun backupIfNecessary() {
Timber.d(">>> backupIfNecessary")
Preferences.getBackupEmail(context)?.let { email ->
this.backupSessionsIfNecessary(email)
this.backupTransactionsIfNecessary(email)
}
}
private fun backupSessionsIfNecessary(email: String) {
if (this.sessionsChanged) {
Timber.d(">>>> backup sessions")
val sessions = this.realm.where(Session::class.java).findAll().sort("startDate")
val csv = ProductCSVDescriptors.pokerAnalyticsAndroid6Sessions.toCSV(sessions)
val fileName = "sessions_${Date().dateTimeFileFormatted}.csv"
CoroutineScope(context = Dispatchers.IO).launch {
if (BackupApi.backupFile(context, email, fileName, csv)) {
sessionsChanged = false
} }
} }
this.transactionsInitialized = true
} }
} }
private fun backupTransactionsIfNecessary(email: String) { private fun backupDataType(dataType: DataType) {
val data = Data.Builder()
.putInt(BackupTask.ParamKeys.DATA.value, dataType.ordinal)
if (this.transactionsChanged) { val backupTask = OneTimeWorkRequestBuilder<BackupTask>()
Timber.d(">>>> backup transactions") .setInitialDelay(10, TimeUnit.HOURS)
val transactions = this.realm.where(Transaction::class.java).findAll().sort("date") // .setInitialDelay(10, TimeUnit.SECONDS)
val csv = ProductCSVDescriptors.pokerAnalyticsAndroidTransactions.toCSV(transactions) .setInputData(data.build())
val fileName = "transactions_${Date().dateTimeFileFormatted}.csv" .addTag(dataType.workId)
.build()
CoroutineScope(context = Dispatchers.IO).launch { Timber.d(">>> create backupTask")
if (BackupApi.backupFile(context, email, fileName, csv)) {
transactionsChanged = false
}
}
}
WorkManager.getInstance(context).enqueueUniqueWork(dataType.workId, ExistingWorkPolicy.REPLACE, backupTask)
} }
} }

@ -0,0 +1,75 @@
package net.pokeranalytics.android.util
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.pokeranalytics.android.api.BackupApi
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.util.csv.DataType
import net.pokeranalytics.android.util.csv.ProductCSVDescriptors
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted
import timber.log.Timber
import java.util.*
class BackupTask(var context: Context, var params: WorkerParameters) : Worker(context, params) {
enum class ParamKeys(val value: String) {
DATA("title"),
}
override fun doWork(): Result {
val data = params.inputData
val dataTypeInt = data.getInt(ParamKeys.DATA.value, 0)
val dataType = DataType.values()[dataTypeInt]
Preferences.getBackupEmail(context)?.let { email ->
when(dataType) {
DataType.SESSION -> {
backupSessions(email)
}
DataType.TRANSACTION -> {
backupTransactions(email)
}
}
}
return Result.success()
}
private fun backupSessions(email: String) {
Timber.d(">>>> backup sessions")
val realm = Realm.getDefaultInstance()
val sessions = realm.where(Session::class.java).findAll().sort("startDate")
val csv = ProductCSVDescriptors.pokerAnalyticsAndroid6Sessions.toCSV(sessions)
val fileName = "sessions_${Date().dateTimeFileFormatted}.csv"
CoroutineScope(context = Dispatchers.IO).launch {
BackupApi.backupFile(context, email, fileName, csv)
}
realm.close()
}
private fun backupTransactions(email: String) {
Timber.d(">>>> backup transactions")
val realm = Realm.getDefaultInstance()
val transactions = realm.where(Transaction::class.java).findAll().sort("date")
val csv = ProductCSVDescriptors.pokerAnalyticsAndroidTransactions.toCSV(transactions)
val fileName = "transactions_${Date().dateTimeFileFormatted}.csv"
CoroutineScope(context = Dispatchers.IO).launch {
BackupApi.backupFile(context, email, fileName, csv)
}
realm.close()
}
}

@ -11,28 +11,37 @@ import org.apache.commons.csv.CSVRecord
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
/** enum class DataType {
* A SessionCSVDescriptor is a CSVDescriptor specialized in parsing Session objects TRANSACTION,
*/ SESSION;
class SessionTransactionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg elements: CSVField) :
PACSVDescriptor<Identifiable>(source, isTournament, *elements) {
private enum class DataType { companion object {
TRANSACTION,
SESSION;
companion object { fun valueForString(type: String): DataType? {
return when (type) {
"Deposit/Payout" -> TRANSACTION
"Cash Game", "Tournament" -> SESSION
else -> null
}
}
}
fun valueForString(type: String): DataType? { val workId: String
return when (type) { get() {
"Deposit/Payout" -> TRANSACTION return when (this) {
"Cash Game", "Tournament" -> SESSION TRANSACTION -> "transaction.work"
else -> null SESSION -> "session.work"
}
} }
} }
} }
/**
* A SessionCSVDescriptor is a CSVDescriptor specialized in parsing Session objects
*/
class SessionTransactionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg elements: CSVField) :
PACSVDescriptor<Identifiable>(source, isTournament, *elements) {
/** /**
* Parses a [record] and return an optional Session * Parses a [record] and return an optional Session

Loading…
Cancel
Save